Sparround

Balanslaşdırma: AVL və Red-Black

Adi BST-nin bütün problemi bir cümlədə yığılır: forma əlavə sırasından asılıdır. Sıralı data gəlirsə, ağac zəncirə çevrilir və O(log n) vədi O(n)-ə çevrilir.

Özü-balanslaşan ağaclar bu problemi kökündən həll edir: hər əlavə və silmədən sonra strukturu bir az düzəldib h = O(log n) zəmanətini saxlayırlar. Zəmanət sözü burada açardır — bu, "adətən belədir" deyil, "həmişə belədir" deməkdir.

Niyə bu qədər vacibdir? 1 milyon element üçün fərq belədir:

  • Balanslaşdırılmış: təxminən 20 müqayisə.
  • Deqradasiya olmuş: təxminən 1 000 000 müqayisə.

Yəni balans ağacı işlək saxlayan yeganə şeydir. Bunun qiyməti isə əlavə/silmə zamanı bir az əlavə işdir — amma o iş də O(log n)-dən çıxmır.

Rotasiya nə edir? Bunu implementasiya kimi yox, şəkil kimi başa düşmək kifayətdir.

Tutaq ki, ağac sağa əyilib: root-un sağ alt-ağacı sol alt-ağacından xeyli hündürdür. Sola rotasiya sağ övladı yuxarı qaldırıb köhnə root-u onun sol övladı edir. Nəticədə hündür budaq bir səviyyə aşağı düşür, qısa budaq bir səviyyə yuxarı qalxır — ağac düzləşir.

Üç vacib fakt:

  • Rotasiya inorder sırasını dəyişmir. Element-lərin nisbi sırası eyni qalır, deməli BST invariantı pozulmur. Bu, rotasiyanın niyə təhlükəsiz olduğunu izah edən əsas fikirdir.
  • Rotasiya O(1) əməliyyatdır — cəmi bir neçə göstərici dəyişir, heç bir alt-ağac gəzilmir.
  • Bir əlavədən sonra ən çoxu bir-iki rotasiya lazım olur; silmədən sonra AVL-də rotasiyalar zənciri root-a qədər gedə bilər, amma bu da O(log n)-dir.

Dörd hal var — LL, RR, LR, RL — və onlardan ikisi (LR, RL) iki ardıcıl rotasiya tələb edir. Müsahibədə bu adları bilmək kifayətdir; hansı göstəricinin hara keçdiyini əzbərləmək tələb olunmur.

XüsusiyyətAVLRed-Black
Balans şərtiHər node üçün sol və sağ hündürlük fərqi ən çox 1Rəng qaydaları; ən uzun yol ən qısadan ən çox 2 dəfə uzundur
Nə qədər balanslıDaha sərt — ağac daha alçaqdırDaha boş — ağac bir az hündür ola bilər
AxtarışBir az sürətli (alçaq ağac)Bir az yavaş
Əlavə / silməDaha çox rotasiya — daha bahalıDaha az rotasiya — daha ucuz
Nə vaxt üstündürOxu çox, yazı az (read-heavy)Yazı və silmə çox (write-heavy)
Harada işlənirBəzi DB indeksləri, in-memory indekslərJava/Kotlin `TreeMap`/`TreeSet`, C++ `std::map`, Linux planlayıcısı

Real runtime-larda hara rast gəlirsən?

  • Kotlin / Java: TreeMapTreeSet red-black tree üzərində qurulub. firstKey(), floorKey(k), ceilingKey(k), subMap(a, b) — bunların hamısı O(log n)-dir və hash map-də ümumiyyətlə mümkün deyil.
  • Dart: SplayTreeMapSplayTreeSet (dart:collection). Splay tree fərqli bir özü-tənzimləyən ailədir — amortizə olunmuş O(log n) verir və son istifadə olunmuş açarları root-a yaxınlaşdırır.
  • JavaScript: standart kitabxanada sıralı map YOXDUR. Sıralı davranış lazımdırsa ya açarları çıxarıb sıralayırsan, ya sıralı massiv + binary search saxlayırsan, ya da kitabxana götürürsən. Bunu bilmək müsahibədə faydalıdır.
  • C++: std::mapstd::set red-black tree-dir.
  • Verilənlər bazaları: indekslər adətən B-tree/B+tree işlədir — bu, eyni ideyanın disk üçün genişləndirilmiş variantıdır: node-lar daha çox açar saxlayır ki, disk oxunuşlarının sayı azalsın.

Hash map ilə fərqi bir cümlə ilə yadda saxla: hash sürət verir, ağac sıra verir.

İnterview ipucu. "AVL ağacını implementasiya et" sualı middle səviyyəli müsahibədə demək olar heç vaxt verilmir — və verilsə belə, gözlənilən cavab tam kod deyil.

Dürüst və güclü cavab belədir: "Rotasiyaların dəqiq göstərici manipulyasiyasını əzbərdən yazmıram — praktikada TreeMap və ya SplayTreeMap istifadə edirəm. Amma prinsipi izah edə bilərəm: hər əlavədən sonra balans faktorunu yoxlayıram, |fərq| > 1 olarsa dörd haldan (LL, RR, LR, RL) birinə uyğun rotasiya edirəm; rotasiya O(1)-dir və inorder sırasını dəyişmir. İstəyirsinizsə, sadə sağa rotasiyanı yazım."

Bu cavab üç şey göstərir: mexanizmi başa düşürsən, sərhədlərini bilirsən, və praktik seçim edə bilirsən. Uydurub yarımçıq rotasiya kodu yazmaqdan qat-qat yaxşıdır.

Ən çox verilən əsl sual isə başqadır: "Niyə balans lazımdır?" Cavab: adi BST-nin forması əlavə sırasından asılıdır və sıralı input onu O(n)-lik zəncirə çevirir. Bir cümlə əlavə et — "Java-nın TreeMap-i məhz buna görə red-black tree-dir" — və cavab tamamlanır.

📚 Mənbələr və sənədlər