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ət | AVL | Red-Black |
|---|---|---|
| Balans şərti | Hər node üçün sol və sağ hündürlük fərqi ən çox 1 | Rə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ır | Daha 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ür | Oxu çox, yazı az (read-heavy) | Yazı və silmə çox (write-heavy) |
| Harada işlənir | Bəzi DB indeksləri, in-memory indekslər | Java/Kotlin `TreeMap`/`TreeSet`, C++ `std::map`, Linux planlayıcısı |
Real runtime-larda hara rast gəlirsən?
- Kotlin / Java:
TreeMapvəTreeSetred-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:
SplayTreeMapvəSplayTreeSet(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::mapvəstd::setred-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
- java.util.TreeMap (Red-Black tree)rəsmidocs.oracle.com
Real balanslaşdırılmış ağac implementasiyası: log(n) zəmanətləri sənəddə açıq yazılıb.
- Dart SplayTreeMaprəsmiapi.dart.dev