Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

Drzewa AVL

SPIS TREŚCI
Podrozdziały

Wiadomości podstawowe

Drzewo binarnych wyszukiwań BST (ang. BST – Binary Search Tree) jest dobrą strukturą danych do szybkiego wyszukiwania elementów. Jeśli wysokość drzewa jest minimalna, to operacja wyszukiwania elementu posiada pesymistyczną klasę złożoności obliczeniowej O(log2n). Jest to wynik bardzo korzystny. Np. wyszukanie danej w drzewie BST zawierającym 1.000.000 elementów wymaga jedynie co najwyżej 20  porównań. Gdybyśmy elementy te umieścili w tablicy lub na liście liniowej, to analogiczne wyszukiwanie wymagałoby co najwyżej 1.000.000 operacji porównań. Co więcej, funkcja logarytmiczna rośnie wolniej od funkcji liniowej, zatem zysk powiększa się przy wzroście liczby elementów – jeśli będzie ich 1000 razy więcej, to w przypadku tablicy i listy czas wyszukiwania wydłuży się 1000 razy (przyrośnie liniowo), natomiast dla drzewa BST otrzymamy co najwyżej 30 operacji porównań (o 10 więcej niż poprzednio).

Niestety, drzewa BST w klasycznej postaci posiadają nieprzyjemną wadę – mogą się degenerować do list liniowych, a wtedy tracimy ich podstawową zaletę – klasę złożoności O(log2n), która w przypadku niekorzystnym może stać się klasą liniową O(n). Problem ten można załagodzić przez stosowanie algorytmów równoważących drzewa BST, np. algorytmu DSW. Niemniej jednak jest to czasami kłopotliwe. Aby rozwiązać problem degeneracji drzew BST, wymyślono różne ich odmiany. Jedną z nich, najstarszą, jest drzewo AVL (ang. AVL Tree). Nazwa bierze się od nazwisk dwóch radzieckich wynalazców Georgija Adelsona-Wielskiego (ros. Гео́ргий Макси́мович Адельсо́н-Ве́льский) oraz Evgenija Landisa (ros. Евгений Михайлович Ландис), którzy w roku 1962 opublikowali pracę na temat drzew AVL.

Drzewo AVL jest drzewem BST, w którym dla każdego węzła wysokość jego poddrzew dla lewego i prawego syna różni się co najwyżej o 1. Aby osiągnąć ten warunek, drzewo AVL posiada zmodyfikowane procedury wstawiania i usuwania węzłów. Równoważenie uzyskuje się poprzez odpowiednie rotacje w lewo i w prawo węzłów drzewa na ścieżce w kierunku korzenia, jeśli został zaburzony warunek drzewa AVL (wysokości poddrzew różnią się co najwyżej o 1) po wstawieniu nowego węzła lub po usunięciu istniejącego węzła.

Każdy węzeł drzewa AVL posiada dodatkowe pole o nazwie bf (ang. balance factor – współczynnik równowagi). Wartość tego pola spełnia równanie:

bf = hL-hR
gdzie: hLhR to odpowiednio wysokości
lewego i prawego poddrzewa.

Z powyższego wzoru możemy wysnuć następujące wnioski:

bf =  1 – lewe poddrzewo jest wyższe
          o jeden poziom od prawego
          poddrzewa,  hL > hR
bf =  0 – oba poddrzewa są równej
          wysokości, hL = hR
bf = -1 – prawe poddrzewo jest wyższe
          o jeden poziom od lewego
          poddrzewa, hL < hR

Innych wartości w drzewie AVL współczynnik bf nie może przyjmować – ograniczenie to nosi nazwę własności drzewa AVL (ang. AVL tree property). Poniżej przedstawiamy dwa drzewa, w  których węzły oprócz kluczy posiadają również pola bf zapisane po dwukropku:

obrazek
Drzewo spełnia własność AVL
 
obrazek
Drzewo nie spełnia własności AVL

W prawym drzewie węzeł o kluczu 20 posiada współczynnik równowagi równy -2, ponieważ dla tego węzła mamy:

hL = 0
hR = 2

bf = hL-hR = 0-2 = -2

Węzły drzew AVL będziemy reprezentowali następującą strukturą:

Pascal
type
  PAVLnode = ^AVLnode;
  AVLnode  = record
    up    : PAVLnode;
    left  : PAVLnode;
    right : PAVLnode;
    key   : typ_danych;
    bf    : integer;
    // opcjonalnie
    Data: typ_danych;
  end;
C++
struct AVLnode
{
  AVLnode * up;
  AVLnode * left;
  AVLnode * right;
  typ_danych key;
  int bf;
  // opcjonalnie
  typ_danych data
};
Basic
Type AVLnode
  up As AVLnode Ptr
  left As AVLnode Ptr
  right As AVLnode Ptr
  key As typ_danych
  bf As Integer
  ' opcjonalnie
  data As typ_danych
End Type
Python (dodatek)
class AVLnode:


    def __init__(self):
        self.up    = None
        self.left  = None
        self.right = None
        self.key   = 0
        self.bf    = 0
        # opcjonalnie
        self.data  = 0

Znaczenie poszczególnych pól jest następujące:

up : adres ojca węzła.
left : adres lewego syna.
right : adres prawego syna.
key : klucz, według którego są uporządkowane węzły drzewa.
bf : współczynnik równowagi.
Data: dane przechowywane w węźle (opcjonalnie)

do podrozdziału  do strony 

Rotacje drzewa AVL

Wyszukiwanie informacji w drzewie AVL niczym nie różni się od wyszukiwania informacji w drzewie BST, zatem możemy stosować tutaj ten sam algorytm. Dotyczy to również znajdowania poprzedników i następników węzła. Różnice pojawiają się przy wstawianiu i usuwaniu węzłów, ponieważ operacje te mogą prowadzić do zmiany wysokości poddrzew. Operacje insert oraz delete dla drzewa AVL składają się z dwóch faz – pierwsza faza jest identyczna jak w przypadku drzewa BST, druga faza ma za zadanie sprawdzić, czy pierwsza faza nie naruszyła własności AVL, a jeśli tak, to własność AVL musi zostać przywrócona.

Własność AVL przywracamy przy pomocy tzw. rotacji, czyli odpowiedniego przemieszczenia 3 węzłów. Istnieją 4 rodzaje rotacji – dwie pojedyncze oraz dwie podwójne:

Oznaczenia RR, LL, RLLR określają sposób połączenia węzłów przed wykonaniem rotacji. Jeśli węzły łączą się prawymi krawędziami, mamy do czynienia z przypadkiem RR, jeśli tylko lewymi, to jest to przypadek LL. Mieszane połączenia są oznaczane jako LRRL.

Rotacje wykonujemy, gdy natrafimy w drzewie AVL na węzeł o parametrze bf równym 2 lub -2. Taka wartość parametru bf świadczy o niezrównoważeniu węzła – jego poddrzewa różnią się wysokością o 2. Rotacja przywraca równowagę w węźle. Załóżmy, iż w wyniku wstawiania lub usuwania otrzymaliśmy następujący układ węzłów:

obrazek

Widzimy wyraźnie, iż węzeł 1 posiada niezrównoważone poddrzewa – lewe jest puste, a prawe ma wysokość 2. Stąd jego parametr bf przyjmuje wartość -2. Dokonujemy rotacji RR węzłów 1, 23 w punkcie zaczepienia 1 i przywracamy równowagę:

obrazek

Węzeł 1 stał się lewym synem węzła 2. Węzeł 2 zajął miejsce węzła 1. Rotacja zmniejszyła o 1 wysokość poddrzewa prawego i zwiększyła o 1 wysokość poddrzewa lewego. Skoro prawe było o dwa poziomy wyższe od lewego, to teraz się zrównały. To jest właśnie cel wykonywania rotacji – zrównoważenie wysokości poddrzew, gdy różnią się one od siebie wysokością o więcej niż 1. Zwróć uwagę, iż rotacja nie zmienia własności drzewa BST ani kolejności przechodzenia inorder. Rotację LL wykonujemy analogicznie – jest ona lustrzanym odbiciem rotacji RR.

Problem pojawia się przy poniższej konfiguracji węzłów (RL):

obrazek

Takiego układu nie można po prostu obrócić obrotem RR, ponieważ wystąpią konflikty pomiędzy niezaznaczonymi na rysunku synami węzłów. Najpierw dokonujemy obrotu LL węzłów 23 (sprowadza on węzły do układu RR), a następnie obracamy RR węzły 12. Całość nosi nazwę obrotu podwójnego RL:

obrazek

Ponieważ pojedynczy obrót nie zmienia własności BST, również obrót podwójny nie zmienia jej, gdyż składa się on z dwóch obrotów pojedynczych.

Podamy teraz dokładne algorytmy wykonywania poszczególnych rotacji węzłów, które uwzględniają posiadanie przez węzły synów. Węzeł biegunowy rotacji oznaczamy kolorem czerwonym.

Rotacja RR

obrazek

W rotacji uczestniczą węzły A B. Węzeł B zajmuje miejsce węzła A, węzeł A staje się lewym synem węzła B. Lewy syn węzła B (BL) staje się prawym synem węzła A. Aby wyznaczyć współczynniki równowagi bf obu węzłów AB po rotacji, rozważmy poniższe przypadki:

obrazek

Przypadek nr 1
A.bf =-2,
B.bf = -1

Zaczynamy od węzła B. Ponieważ B.bf = -1, to przeważa prawa gałąź BR. Wynika to bezpośrednio ze wzoru na współczynnik równowagi:

B.bf = hBL-hBR, 
B.bf = -1, 
hBL-hBR = -1, 
hBR = hBL+1

Oznaczmy wysokość lewego poddrzewa węzła B przez h, czyli hBL = h. Wtedy prawe poddrzewo węzła B będzie posiadało wysokość hBR = h+1, o jeden większą od lewego poddrzewa.

Teraz obliczymy wysokość poddrzewa, którego korzeniem jest węzeł B. Wzór jest bardzo prosty – do wysokości najwyższego poddrzewa dodajemy jedynkę, czyli:

hB = 1+max(hBL, hBR)

U nas najwyższe jest prawe poddrzewo, zatem:

hB = 1+hBR = 1+h+1 = h+2

Przechodzimy do węzła A. Ponieważ jego współczynnik równowagi wynosi -2, to wnioskujemy, iż przeważa o dwa poziomy prawe poddrzewo A:

A.bf = hAL-hAR, 
A.bf = -2, 
hAL-hAR = -2, 
hAL = hAR-2

Prawe poddrzewo węzła A jest poddrzewem, którego korzeń znajduje się w wierzchołku B. Stąd:

hAR = hB = h+2

I ostatecznie otrzymujemy:

hAL = hAR-2 = h+2-2 = h

Wyznaczyliśmy względne wysokości wszystkich synów wierzchołków AB dla rozważanego przypadku:

hAL = h
hBL = h
hBR = h+1

Podkreślam, że nie interesują nas wartości h – to zależy już od konkretnego drzewa AVL. Mając wyznaczone względne wysokości poddrzew możemy wyliczyć współczynniki równowagi bf węzłów AB po rotacji:

A.bf = hAL-hBL = h-h = 0
B.bf = hA-hBR = h+1-(h+1) = 0
obrazek

Przypadek nr 2
A.bf = -2,
B.bf = 0

Wykonując podobne operację (wykonaj je sam jako ćwiczenie) w przypadku drugim dochodzimy do wniosku, iż po rotacji węzły AB otrzymają następujące współczynniki równowagi:

A.bf = -1
B.bf = 1
obrazek

Przypadek nr 3
A.bf = -2,
B.bf = 1

W przypadku nr 3 otrzymujemy po rotacji:

A.bf = -1
B.bf = 2

Wynika z tego prosty wniosek – w takim układzie współczynników równowagi węzłów rotacja RR nie rozwiązuje problemu równowagi drzewa AVL. Nie będziemy zatem jej stosować dla przypadku nr 3! Zamiast niej będą stosowane rotacje podwójne.

Podsumujmy zatem:

Przed rotacją
A.bf -2 -2
B.bf -1 0
Po rotacji RR
A.bf 0 -1
B.bf 0 1

Algorytm rotacji RR dla drzewa AVL

Wejście:

root : referencja do zmiennej, która przechowuje adres korzenia drzewa AVL.
A : wskazanie węzła głównego rotacji.

Wyjście:

Wykonanie rotacji RR.

Dane pomocnicze:

B : wskazanie węzła B.
p : wskazanie ojca A.

Lista kroków:

K01: BAright ; w B umieszczamy adres
     ; prawego syna węzła A
K02: pAup ; w p umieszczamy ojca A
K03: Aright ← Bleft ; prawym synem A
     ; staje się lewy syn B
K04: Jeśli Arightnil, ; jeśli prawy syn 
     to ArightupA ; istnieje, to jego ojcem 
     ; jest teraz węzeł A
K05: Bleft ← A ; lewym synem B staje się A
K06: Bup ← p ; ojcem B jest ojciec węzła A
K07: Aup ← B ; natomiast A zmienia ojca na B
K08: Jeśli p = nil, ; sprawdzamy, czy węzeł A
     to idź do kroku K11 ; był korzeniem
K09: Jeśli pleft = A, ; jeśli nie, to
     to pleft ← B ; uaktualniamy jego ojca 
     inaczej pright ← B
K10: Idź do kroku K12
K11: root ← B ; jeśli A był korzeniem, 
     ; to uaktualniamy korzeń
K12: Jeśli Bbf = -1, 
     to Abf ← 0 ; modyfikujemy współczynniki
        Bbf ← 0 ; równowagi
     inaczej Abf ← -1
             Bbf ← 1
K13: Zakończ

Rotacja LL

obrazek

Rotacja LL jest lustrzanym odbiciem rotacji RR. W rotacji uczestniczą węzły AB. Węzeł B zajmuje miejsce węzła A, węzeł A staje się prawym synem węzła B. Prawy syn węzła B (BR) staje się lewym synem węzła A. Współczynniki równowagi węzłów AB po rotacji wyznaczamy w identyczny sposób, jak przy rotacji RR – można je również wywnioskować na podstawie własności symetrii tych rotacji. Proponuję wykonanie tych działań jako ćwiczenie samodzielne. My otrzymaliśmy następujące wyniki:

Przed rotacją
A.bf 2 2
B.bf 1 0
Po rotacji RR
A.bf 0 1
B.bf 0 -1

Algorytm rotacji LL dla drzewa AVL

Wejście:

root : referencja do zmiennej, która przechowuje adres korzenia drzewa AVL.
A : wskazanie węzła głównego rotacji.

Wyjście:

Wykonanie rotacji LL.

Dane pomocnicze:

B : wskazanie węzła B.
p : wskazanie ojca A.

Lista kroków:

K01: BAleft ; w B umieszczamy adres
     ; lewego syna węzła A
K02: pAup ; w p umieszczamy ojca A
K03: Aleft ← Bright ; lewym synem A
     ; staje się prawy syn B
K04: Jeśli Aleftnil, ; jeśli lewy syn 
     to AleftupA ; istnieje, to jego
     ; ojcem jest teraz A
K05: Bright ← A ; prawym synem B
     ; staje się A
K06: Bup ← p ; ojcem B jest ojciec węzła A
K07: Aup ← B ; natomiast A zmienia ojca na B
K08: Jeśli p = nil, ; sprawdzamy, czy węzeł A
     to idź do kroku K11 ; był korzeniem
K09: Jeśli pleft = A, ; jeśli nie, 
     to pleft ← B ; to uaktualniamy jego ojca 
     inaczej pright ← B
K10: Idź do kroku K12
K11: root ← B ; jeśli A był korzeniem, 
     ; to uaktualniamy korzeń
K12: Jeśli Bbf = 1, 
     to Abf ← 0 ; modyfikujemy współczynniki
        Bbf ← 0 ; równowagi
     inaczej Abf ← 1
             Bbf ← -1
K13: Zakończ

Rotacja RL

obrazek

Rotacja RL jest złożeniem rotacji LL i rotacji RR, dlatego nosi nazwę rotacji podwójnej. Pierwsza rotacja LL sprowadza gałąź drzewa do postaci dogodnej dla następnej rotacji RR. Zamiast jednak wykonywać kolejno rotację RR, a następnie LL, zaprojektujemy algorytm, który od razu przekształca pozycje węzłów do pozycji końcowej. Węzeł C zastępuje węzeł A. Węzeł A staje się lewym dzieckiem węzła C i przejmuje lewego syna węzła C w miejsce swojego prawego syna. Węzeł B staje się prawym synem C i przejmuje prawego syna węzła C w miejsce swojego lewego syna. Określenie współczynników bf po rotacji wykonamy podobnie jak przy rotacjach pojedynczych – przeanalizujemy wysokości poddrzew przed i po rotacji podwójnej.

obrazek

Przypadek nr 1
A.bf = -2,
B.bf = 1
C.bf = -1

Współczynniki bf węzłów A, BC po rotacji wyznaczamy w podobny sposób, jak w przypadku rotacji RR. Rozpoczynamy od najniższego węzła C:

C.bf = hCL-hCR
C.bf = -1
hCR = hCL+1

Zatem lżejsze jest poddrzewo lewe. Oznaczmy:

hCL = h
hCR = h+1
hC = h+2

Teraz przechodzimy do węzła B:

B.bf = hBL-hBR
B.bf = 1
hBR = hBL-1 = hC-1 = (h+2)-1 = h+1
hB = h+3

Na koniec pozostaje węzeł A:

A.bf = hAL-hAR
A.bf = -2
hAL = hAR-2 = hB-2 = (h+3)-2 = h+1

Wyznaczyliśmy względne wysokości poddrzew wierzchołków A, BC:

hAL = h+1
hBR = h+1
hCL = h
hCR = h+1

Możemy zatem obliczyć współczynniki równowagi bf wierzchołków A, BC po wykonaniu rotacji RL:

A.bf = hAL-hCL = (h+1)-h = 1
B.bf = hA-hC = (h+2)-(h+2) = 0
C.bf = hCR-hBR = (h+1)-(h+1) = 0
obrazek

Przypadek nr 2
A.bf = -2
B.bf = 1
C.bf = 0

W przypadku nr 2 nie trudno obliczyć wyżej podanym sposobem (zrób to jako ćwiczenie), iż po rotacji RL wierzchołki A, B C przyjmą następujące współczynniki równowagi:

A.bf = 0
B.bf = 0
C.bf = 0
obrazek

Przypadek nr 3
A.bf = -2
B.bf = 1
C.bf = 1

W przypadku nr 3 otrzymujemy:

A.bf =  0
B.bf = -1
C.bf =  0

Podsumujmy otrzymane wyniki:

Przed rotacją RL
A.bf -2 -2 -2
B.bf 1 1 1
C.bf -1 0 1
Po rotacji RL
A.bf 1 0 0
B.bf 0 0 -1
C.bf 0 0 0

Z tabelki wynika, iż współczynnik równowagi C.bf jest zawsze zerowany, a pozostałe zależą od początkowej wartości C.bf.

Algorytm rotacji RL dla drzewa AVL

Wejście:

root : referencja do zmiennej przechowującej adres korzenia drzewa AVL.
A : wskazanie węzła głównego rotacji.

Wyjście:

Wykonanie rotacji RL

Dane pomocnicze:

B : wskazanie węzła B.
C : wskazanie węzła C.
p : wskazanie ojca A.

Lista kroków:

K01: BAright ; ustawiamy adresy węzłów
     ; biorących udział w rotacji
K02: CBleft
K03: pAup
K04: BleftCright ; lewym synem B
     ; staje się prawy syn C
K05: Jeśli Bleftnil, ; jeśli lewy syn B
     to BleftupB ; istnieje, to B staje się
     ; jego ojcem 
K06: ArightCleft ; prawym synem A
     ; staje się lewy syn C
K07: Jeśli Arightnil, ; jeśli prawy syn A
     to ArightupA ; istnieje, to A staje się
     ; jego ojcem 
K08: CleftA ; lewym synem C staje się A
K09: CrightB ; prawym synem C staje się B
K10: AupC ; ojcem A staje się C
K11: BupC ; ojcem B staje się C
K12: Cupp ; ojcem C staje się były ojciec A
K13: Jeśli p = nil, ; sprawdzamy, czy A był korzeniem
     to idź do kroku K16
K14: Jeśli pleft = A, ; jeśli nie, to uaktualniamy
     to pleftC ; byłego ojca A, aby był
     inaczej prightC ; ojcem C
K15: Idź do kroku K17
K16: rootC ; w przeciwnym razie uaktualniamy korzeń
K17: Jeśli Cbf = -1, 
     to Abf ← 1      ; uaktualniamy współczynniki
     inaczej Abf ← 0 ; równowagi
K18: Jeśli Cbf = 1, 
     to Bbf ← -1
     inaczej Bbf ← 0
K19: Cbf ← 0
K20: Zakończ

Rotacja LR

obrazek

Podobnie jak w przypadku rotacji pojedynczych RRLL, rotacja LR jest lustrzanym odbiciem rotacji RL. Węzeł C zastępuje węzeł A. Węzeł A staje się prawym synem węzła C i przejmuje on prawego syna CR w miejsce swojego lewego syna. Węzeł B staje się lewym synem C i przejmuje on lewego syna C w miejsce swojego prawego syna. Współczynniki równowagi zmieniają się następująco (proponuję ci sprawdzenie tych wyników):

Przed rotacją LR
A.bf 2 2 2
B.bf -1 -1 -1
C.bf -1 0 1
Po rotacji LR
A.bf 0 0 -1
B.bf 1 0 0
C.bf 0 0 0

Współczynnik równowagi C.bf jest zawsze zerowany, a pozostałe zależą od początkowej wartości C.bf.

Algorytm rotacji LR dla drzewa AVL

Wejście:

root : referencja do zmiennej przechowującej adres korzenia drzewa AVL.
A : wskazanie węzła głównego rotacji.

Wyjście:

Wykonanie rotacji LR.

Dane pomocnicze:

B : wskazanie węzła B.
C : wskazanie węzła C.
p : wskazanie ojca A.

Lista kroków:

K01: BAleft ; ustawiamy adresy węzłów biorących
     ; udział w rotacji
K02: CBright
K03: pAup
K04: BrightCleft ; prawym synem B staje się
     ; lewy syn C
K05: Jeśli Brightnil, ; jeśli prawy syn B istnieje, 
     to BrightupB ; to B staje się jego ojcem 
K06: AleftCright ; lewym synem A staje się
     ; prawy syn C
K07: Jeśli Aleftnil, ; jeśli lewy syn A istnieje, 
     to AleftupA ; to A staje się jego ojcem 
K08: CrightA ; prawym synem C staje się A
K09: CleftB ; lewym synem C staje się B
K10: AupC ; ojcem A staje się C
K11: BupC ; ojcem B staje się C
K12: Cupp ; ojcem C staje się były ojciec A
K13: Jeśli p = nil, ; sprawdzamy, czy A był korzeniem
     to idź do kroku K16
K14: Jeśli pleft = A,  ; jeśli nie, to uaktualniamy
     to pleftC ; byłego ojca A, aby był ojcem C
     inaczej prightC
K15: Idź do kroku K17
K16: rootC ; w przeciwnym razie uaktualniamy korzeń
K17: Jeśli Cbf = 1, 
     to Abf ← -1 ; uaktualniamy współczynniki równowagi
     inaczej Abf ← 0
K18: Jeśli Cbf = -1, 
     to Bbf ← 1
     inaczej Bbf ← 0
K19: Cbf ← 0
K20: Zakończ

do podrozdziału  do strony 

Wstawianie węzła do drzewa AVL

Operacja wstawiania węzła do drzewa AVL jest dwuetapowa. Pierwszy etap jest identyczny z operacją wstawiania węzła do drzewa BST. Następnie, idąc w górę w kierunku korzenia drzewa, musimy odpowiednio zmodyfikować współczynniki równowagi węzłów i jeśli natrafimy na współczynnik bf równy 2 lub -2, to za pomocą rotacji zrównoważamy drzewo AVL – rotacja powoduje przywrócenie danemu poddrzewu wysokości sprzed wykonania operacji wstawiania. Zatem po wykonaniu tej rotacji drzewo AVL jest zrównoważone i możemy zakończyć wstawianie elementu.

Algorytm wstawiania węzła do drzewa AVL

Wejście:

root : referencja do zmiennej, która przechowuje adres korzenia drzewa AVL.
k : klucz wstawianego węzła.

Wyjście:

Drzewo AVL z nowym węzłem o kluczu k.

Elementy pomocnicze:

w, p, r : wskazania węzłów.
rot_ll(roor, x);rot_rr(root, x);
rot_lr(
root, x);rot_rl(root, x) : rotacje węzłów, x jest wskazaniem węzła głównego rotacji, root jest wskazaniem korzenia drzewa AVL.

Lista kroków:

K01: Utwórz nowy węzeł
K02: w ← adres nowego węzła
K03: wleftnil ; inicjujemy pola węzła
K04: wrightnil
K05: wkeyk
K06: wbf ← 0
K07: proot
K08: Jeśli p = nil, ; sprawdzamy, czy drzewo jest puste
     to rootw i zakończ
K09:   Jeśli k < pkey, ; porównujemy klucze
       to idź do kroku K13
K10:   Jeśli pright = nil, ; jeśli prawy syn nie istnieje, 
       to prightw ; to nowy węzeł staje się prawym synem 
          i idź do kroku K16 ; i wychodzimy z pętli
K11:   ppright ; inaczej przechodzimy do prawego syna 
K12:   Idź do kroku K09 ; i kontynuujemy pętlę
K13:   Jeśli pleft = nil,  ; to samo dla lewego syna 
      to pleftw
          i idź do kroku K16
K14:   ppleft
K15:   Idź do kroku K09
K16: wupp ; uzupełniamy ojca węzła
K17: Jeśli pbf = 0, ; modyfikujemy współczynniki równowagi
     to idź do kroku K20 ; w kierunku korzenia
K18: pbf ← 0 ; patrz: UWAGA NR 1
K19: Zakończ
K20: Jeśli pleft = w, ; patrz: UWAGA NR 2
     to pbf ← 1
     Inaczej pbf ← -1
K21: rpup ; będziemy się przemieszczać w górę drzewa AVL
     ; w poszukiwaniu węzła, który stracił równowagę w wyniku
     ; dodania elementu n.
K22: Dopóki rnil: ; wskazania r i p to rodzić i syn 
     wykonuj kroki K23…K26 ; na tej ścieżce
K23:   Jeśli rbf ≠ 0, ; jeśli r.bf = 0, to obie gałęzie r
       to idź do kroku K28 ; były w równowadze przed dodaniem węzła
K24:   Jeśli rleft = p, ; zwiększamy lub zmniejszamy r.bf
       to rbf ← 1 ; w zależności od tego, w której gałęzi węzła r
       inaczej rbf ← -1 ; został dodany węzeł w. Gałąź ta jest cięższa!
K25:   pr ; przemieszczamy się w górę drzewa
K26:   rrup
K27: Zakończ
K28: Jeśli rbf = -1, ; sprawdzamy, która z gałęzi r była cięższa
     to idź do kroku K32
K29: Jeśli rright = p, ; prawa gałąź ze wstawionym elementem
     to rbf ← 0 i zakończ ; równoważy lewą gałąź – kończymy
K30: Jeśli pbf = -1, ; patrz UWAGA NR 3
     to rot_lr(root, r)
     inaczej rot_ll(root, r)
K31: Zakończ
K32: Jeśli rleft = p, ; lewa gałąź ze wstawionym elementem
     to rbf ← 0 ; równoważy prawą gałąź
        i zakończ
K33: Jeśli pbf = 1, ; patrz: UWAGA NR 4
     to rot_rl(root, r)
     inaczej rot_rr(root, r)
K34: Zakończ
obrazek UWAGA NR 1: w kroku K17 sprawdzamy współczynnik
równowagi bf ojca wstawionego węzła w. Jeśli jest
on różny od zera, to go zerujemy i kończymy. Wyjaśnienie
jest bardzo proste. Współczynnik bf różny od zera może
posiadać w drzewie AVL wartości tylko -1 lub 1. Ponieważ
dołączyliśmy nowy węzeł do pustego liścia, to przed operacją
w gałęzi tej nie było żadnego poddrzewa – zatem wysokość
jego była równa 0. Skoro bf ojca jest różny od 0, to
w drugiej gałęzi musiał być dokładnie jeden węzeł, co dawało
poddrzewo o wysokości 1. Po dodaniu nowego węzła w obu
gałęziach, lewej i prawej, mamy po jednym węźle, zatem
współczynnik bf ich ojca musi przyjąć wartość 0.
Drzewo AVL zostaje zrównoważone, kończymy operację.
obrazek UWAGA NR 2: w kroku K20 wiemy, że przed operacją
dodawania nowego węzła węzeł p miał współczynnik bf
równy 0. Ponieważ dodaliśmy nowy węzeł w do pustego
liścia węzła p, zatem druga gałąź również była pusta.
Sprawdzamy, która gałąź p stała się cięższa (zwiększyła
się wysokość poddrzewa)
po dodaniu nowego elementu w.
W zależności od wyniku ustawiamy współczynnik bf ojca
wstawionego węzła na 1 (nowy węzeł w jest w lewej gałęzi
węzła p i ona jest teraz cięższa)
lub na -1 (nowy węzeł w jest
w prawej gałęzi węzła p)
.
Wstawianie węzła UWAGA NR 3: w kroku K30 wiemy, że przeważało lewe
poddrzewo węzła r i jest to poddrzewo, w którym wstawiliśmy
nowy element, ponieważ węzeł p stanowi jego korzeń. Musimy
skorygować zrównoważenie wykonując rotację. Rodzaj rotacji
wybieramy w zależności od tego, które poddrzewo węzła p jest
cięższe – jeśli lewe, to mamy rotację pojedynczą LL, a jeśli
prawe, to musimy wykonać rotację podwójną LR.
UWAGA NR 4: tutaj mamy lustrzane odbicie sytuacji opisanej
powyżej.

do podrozdziału  do strony 

Usuwanie węzła z drzewa AVL

Usuwanie węzła z drzewa AVL jest operacją dosyć skomplikowaną, ponieważ wymaga rozważenia dużej liczby przypadków przy przywracaniu równowagi. Przy wstawianiu węzła wystarczyło na ścieżce wiodącej od tego węzła do korzenia wykryć konfigurację węzłów, których rotacja przywracała równowagę w drzewie. Po wykonaniu rotacji drzewo AVL znów było w równowadze i operacja wstawiania mogła się zakończyć. Przy usuwaniu węzła mamy nieco inną sytuację. Tutaj poddrzewo, w którym dokonaliśmy usunięcia węzła, zmniejsza swoją wysokość, co może propagować się w górę aż do samego korzenia drzewa. Jedna rotacja zatem może nie być wystarczająca do przywrócenia równowagi w drzewie – rotacje należy kolejno wykonywać dla coraz wyższych węzłów dotąd, aż zniwelujemy zmniejszenie wysokości poddrzewa. Po drodze należy rozważać wiele różnych przypadków konfiguracji węzłów.

Zasada działania algorytmu usuwającego węzeł jest następująca:

Usuwamy węzeł x z drzewa AVL.

obrazek
obrazek
obrazek
obrazek
obrazek

Algorytm usuwania węzła z drzewa AVL – avl_remove(root, x)

Wejście:

root : referencja do zmiennej, która przechowuje adres korzenia drzewa AVL.
x : wskazanie węzła do usunięcia.

Wyjście:

Wskazanie x oraz drzewo AVL bez węzła x.

Elementy pomocnicze:

t, y, z : wskazania węzłów.
rot_ll(root, x), rot_rr(root, x)
rot_lr(root, x), rot_rl(root, x) : rotacje węzłów, x jest wskazaniem węzła głównego rotacji.
pred(x) : zwraca wskazanie poprzednika węzła x.
nest : zmienne logiczna, która wskazuje zagnieżdżenie.

Lista kroków:

K01: Jeśli (xleft = 0)obrazek(xright = 0), ; czy węzeł x posiada obu synów?
     to idź do kroku K05
K02: yavl_remove(root, pred(x)) ; rekurencyjnie usuwamy poprzednik x, 
     ; zapamiętując go w y
K03: nest ← false ; zerujemy zagnieżdżenie
K04: Idź do kroku K08
K05: Jeśli xleftnil, ; węzeł posiada jednego syna lub wcale
     to yxleft
        xleftnil
     inaczej yxright
             xrightnil
K06: xbf ← 0 ; x.bf = 0, ponieważ ewentualny syn trafił do y
K07: nest ← true ; ustawiamy zagnieżdżenie
K08: Jeśli y = nil, ; jeśli y istnieje, to wstawiamy go za x
     to idź do kroku K15
K09: yupxup ; ojcem y staje się ojciec węzła x
K10: yleftxleft ; lewy syn x staje się lewym synem y
K11: Jeśli yleftnil, ; jeśli lewy syn istnieje, 
     to yleftupy ; to jego ojcem staje się y
K12: yrightxright ; to samo dla prawego syna 
K13: Jeśli yrightnil, 
     to yrightupy
K14: ybfxbf
K15: Jeśli xup = nil, ; czy x posiada ojca?
     to idź do kroku K18
K16: Jeśli xupleft = x, ; synem tego ojca staje się y
     to xuplefty
     inaczej xuprighty
K17: Idź do K19
K18: rooty ; inaczej y wstawiamy do korzenia
K19: Jeśli nest = false, ; sprawdzamy zagnieżdżenie
     to idź do kroku K43
K20: zy ; ustawiamy wskaźniki, 
     ; będziemy szli w kierunku korzenia drzewa
K21: yxup
K22: Dopóki ynil, ; w pętli sprawdzamy różne przypadki
     wykonuj kroki K23…K42
K23:   Jeśli ybf ≠ 0, 
       to idź do kroku K26
K24:   Jeśli yleft = z, ; przypadek nr 1
       to ybf ← -1
K25    Idź do K43 ; przerywamy pętlę
K26:   Jeśli ((ybf ≠ 1)obrazek(yleftz))obrazek
             ((ybf ≠ -1)obrazek(yrightz)), 
       to idź do kroku K31
K27:   ybf ← 0 ; przypadek 2
K28:   zy ; przechodzimy na wyższy poziom drzewa
K29:   yyup
K30:   Następny obieg pętli K22
K31:   Jeśli yleft = z, ; t wskazuje syna y przeciwnego do z
       to tyright
       inaczej tyleft
K32:   Jeśli tbf = 0, 
       to idź do kroku K35
K33:   Jeśli ybf = 1, ; przypadek 3A
       to rot_ll(root, y)
       Inaczej rot_rr(root, y)
K34:   Idź do kroku K43 ; przerywamy pętlę
K35:   Jeśli ybftbf, 
       to idź do kroku K40
K36:   Jeśli ybf = 1, ; przypadek 3B
       to rot_ll(root, y)
       inaczej rot_rr(root, y)
K37:   zt ; idziemy na wyższy poziom
K38:   ytup
K39:   Następny obieg pętli K22
K40:   Jeśli ybf = 1, ; przypadek 3C
       to rot_lr(root, y)
       inaczej rot_rl(root, y)
K41:   zyup ; idziemy na wyższy poziom i kontynuujemy pętlę
K42:   y zup
K43: Zakończ z wynikiem x

Przykładowe programy

Uwaga:

Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich.

Program tworzy drzewo AVL z 32 węzłów o kluczach od 1 do 32. Wyświetla je. Następnie usuwa z niego 15 losowych węzłów i wyświetla ponownie drzewo AVL.
Pascal
// Drzewo AVL
// Data: 22.05.2013
// (C)2013 mgr Jerzy Wałaszek
//---------------------------

program AVL;

// Definicja typu węzłów
// drzewa AVL
//----------------------
type
  PAVLnode = ^AVLnode;
  AVLnode  = record
    up    : PAVLnode;
    left  : PAVLnode;
    right : PAVLnode;
    key   : integer;
    bf    : integer;
  end;

// Zmienne globalne
//-----------------

// łańcuchy do znaków ramek
var
  cr, cl, cp : string;

// Procedura wypisuje drzewo
//--------------------------
procedure print_bt(sp, sn : string;
                  v : PAVLnode);
var
  s : string;
  i : integer;
begin
  if v <> nil then
  begin
    s := sp;
    if sn = cr then
      s[length(s)-1] := ' ';
    print_bt(s+cp, cr, v^.right);

    s := Copy(sp, 1, length(sp)-2);
    for i := 1 to length(s) do
      write(char(ord(s[i])));
    for i := 1 to length(sn) do
      write(char(ord(sn[i])));
    writeln(v^.key, ':', v^.bf);

    s := sp;
    if sn = cl then
      s[length(s)-1] := ' ';
    print_bt(s+cp, cl, v^.left);
  end;
end;

// Procedura DFS:postorder
// usuwająca drzewo
//------------------------
procedure dfs_release(v : PAVLnode);
begin
  if v <> nil then
  begin
    // usuwamy lewe poddrzewo
    dfs_release(v^.left);
    // usuwamy prawe poddrzewo
    dfs_release(v^.right);
    // usuwamy sam węzeł
    dispose(v);
  end
end;

// Rotacja RR
//-----------
procedure rot_rr(var root : PAVLnode;
                 A : PAVLnode);
var
  B, p : PAVLnode;
begin
  B := A^.right;
  p := A^.up;

  A^.right := B^.left;
  if A^.right <> nil then
    A^.right^.up := A;

  B^.left := A;
  B^.up := p;
  A^.up := B;

  if p <> nil then
  begin
    if p^.left = A then
      p^.left := B
    else
      p^.right := B;
  end
  else
    root := B;

  if B^.bf = -1 then
  begin
    A^.bf := 0;
    B^.bf := 0;
  end
  else
  begin
    A^.bf := -1;
    B^.bf := 1;
  end
end;

// Rotacja LL
//-----------
procedure rot_ll(var root : PAVLnode;
                 A : PAVLnode);
var
  B, p : PAVLnode;
begin
  B := A^.left;
  p := A^.up;

  A^.left := B^.right;
  if A^.left <> nil then
    A^.left^.up := A;

  B^.right := A;
  B^.up := p;
  A^.up := B;

  if p <> nil then
  begin
    if p^.left = A then
      p^.left := B
    else
      p^.right := B;
  end
  else
    root := B;

  if B^.bf = 1 then
  begin
    A^.bf := 0;
    B^.bf := 0;
  end
  else
  begin
    A^.bf := 1;
    B^.bf := -1;
  end
end;

// Rotacja RL
//-----------
procedure rot_rl(var root : PAVLnode;
                 A : PAVLnode);
var
  B, C, p : PAVLnode;
begin
  B := A^.right;
  C := B^.left;
  p := A^.up;

  B^.left := C^.right;
  if B^.left <> nil then
    B^.left^.up := B;

  A^.right := C^.left;
  if A^.right <> nil then
    A^.right^.up := A;

  C^.left := A;
  C^.right := B;
  A^.up := C;
  B^.up := C;
  C^.up := p;

  if p <> nil then
  begin
    if p^.left = A then
      p^.left := C
    else
      p^.right := C;
  end
  else
    root := C;

  if C^.bf = -1 then
    A^.bf :=  1
  else
    A^.bf := 0;

  if C^.bf =  1 then
    B^.bf := -1
  else
    B^.bf := 0;

  C^.bf := 0;
end;

// Rotacja LR
//-----------
procedure rot_lr(var root : PAVLnode;
                 A : PAVLnode);
var
  B, C, p : PAVLnode;
begin
  B := A^.left;
  C := B^.right;
  p := A^.up;

  B^.right := C^.left;
  if B^.right <> nil then
    B^.right^.up := B;

  A^.left := C^.right;
  if A^.left <> nil then
    A^.left^.up := A;

  C^.right := A;
  C^.left := B;
  A^.up := C;
  B^.up := C;
  C^.up := p;

  if p <> nil then
  begin
    if p^.left = A then
      p^.left := C
    else
      p^.right := C;
  end
  else
    root := C;

  if C^.bf =  1 then
    A^.bf := -1
  else
    A^.bf := 0;

  if C^.bf = -1 then
    B^.bf :=  1
  else
    B^.bf := 0;

  C^.bf := 0;
end;

// Wstawia nowy węzeł do drzewa AVL
// root - referencja korzenia
// k    - klucz nowego węzła
//---------------------------------
procedure avl_insert(var root : PAVLnode;
                        k : integer);
var
  w, p, r : PAVLnode;
  t     : boolean;
begin
  // tworzymy dynamicznie nowy węzeł
  new(w);
  // inicjujemy pola
  w^.left  := nil;
  w^.right := nil;
  w^.up    := nil;
  w^.key   := k;
  w^.bf    := 0;

  //----------------------------------------
  // FAZA 1 - wstawienie węzła do drzewa AVL
  //----------------------------------------

  // rozpoczynamy od korzenia
  p := root;

  // jeśli drzewo jest puste, to węzeł w
  if p = nil then
    root := w // umieszczamy w korzeniu
  else
  begin // inaczej szukamy miejsce dla w
    while true do
      // porównujemy klucze
      if k < p^.key then
      begin
        // jeśli p nie posiada
        // lewego syna, to
        if p^.left = nil then
        begin
          // lewym synem p
          // staje się węzeł w
          p^.left := w;
          break; // wychodzimy z pętli
        end;
        // inaczej przechodzimy
        // do lewego syna 
        p := p^.left;
      end
      else
      begin
        // jeśli p nie posiada
        // prawego syna, to
        if p^.right = nil then
        begin
          // prawym synem 
          // staje się węzeł w
          p^.right := w;
          break; // wychodzimy z pętli
        end;
        // inaczej przechodzimy
        // do prawego syna 
        p := p^.right;
      end;
    w^.up := p; // ojcem w jest p

    //---------------------------------
    // FAZA 2 - równoważenie drzewa AVL
    //---------------------------------

    if p^.bf <> 0 then
      p^.bf := 0 // UWAGA NR 1
    else
    begin
      if p^.left = w then // UWAGA NR 2
        p^.bf := 1
      else
        p^.bf := -1;
      // będziemy szli w górę drzewa
      // w kierunku korzenia
      // r i p wskazują ojca 
      // i syna na tej ścieżce
      r := p^.up;
      t := false;
      while r <> nil do
      begin
        if r^.bf <> 0 then
        begin
          // ustalamy wynik pętli
          t := true;
          break; // przerywamy pętlę
        end;
        // inaczej modyfikujemy r.bf
        if r^.left = p then
          r^.bf :=  1
        else
          r^.bf := -1;
        // przechodzimy w górę
        // na wyższy poziom
        p := r;
        r := r^.up;
      end;

      // jeśli r.bf = +/- 1, 
      // to musimy
      // równoważyć drzewo
      if t then
      begin
        if r^.bf = 1 then
        begin
          // gałęzie się równoważą
          if r^.right = p then
            r^.bf := 0
          else
            if p^.bf = -1 then
              rot_lr(root, r)
            else
              rot_ll(root, r);
        end
        else
        begin // r.bf = -1
          // gałęzie się równoważą
          if r^.left = p then
            r^.bf := 0
          else
            if p^.bf = 1 then
              rot_rl(root, r)
            else
              rot_rr(root, r);
        end;
      end;
    end;
  end;
end;

// Funkcja znajduje
// poprzednik węzła p
//-------------------
function avl_pred(p : PAVLnode)
                   : PAVLnode;
var
  r : PAVLnode;
begin
  if p <> nil then
  begin
    if p^.left <> nil then
    begin
      p := p^.left;
      while p^.right <> nil do
        p := p^.right;
    end
    else
    repeat
      r := p;
      p := p^.up;
    until (p = nil) or (p^.right = r);
  end;
  avl_pred := p;
end;

// Funkcja szuka w drzewie AVL
// węzła o zadanym kluczu. Jeśli
// go znajdzie, zwraca jego wskazanie.
// Jeżeli nie, to zwraca wskazanie puste.
// Parametrami są:
// p - wskazanie korzenia drzewa AVL
// k - klucz poszukiwanego węzła
//---------------------------------------
function avl_find(p : PAVLnode;
                 k : integer)
                   : PAVLnode;
begin
  while (p <> nil) and
        (p^.key <> k) do
    if k < p^.key then
      p := p^.left
    else
      p := p^.right;
  avl_find := p;
end;

// Funkcja usuwa rekurencyjnie węzeł x
// root - referencja do zmiennej
//        z adresem korzenia
// x - wskazanie usuwanego węzła
//------------------------------------
function avl_remove(var root : PAVLnode;
                          x : PAVLnode)
                            : PAVLnode;
var
  t, y, z : PAVLnode;
  nest  : boolean;

begin
  if (x^.left <> nil) and
     (x^.right <> nil) then
  begin
    y := avl_remove(root, avl_pred(x));
    nest := false;
  end
  else
  begin
    if x^.left <> nil then
    begin
      y := x^.left;
      x^.left := nil;
    end
    else
    begin
      y := x^.right;
      x^.right := nil;
    end;
    x^.bf := 0;
    nest := true;
  end;

  if y <> nil then
  begin
    y^.up := x^.up;
    y^.left := x^.left;
    if y^.left <> nil then
      y^.left^.up  := y;

    y^.right := x^.right;
    if y^.right <> nil then
      y^.right^.up := y;

    y^.bf := x^.bf;
  end;

  if x^.up <> nil then
  begin
    if x^.up^.left = x then
      x^.up^.left := y
    else
      x^.up^.right := y;
  end
  else
    root := y;

  if nest then
  begin
    z := y;
    y := x^.up;
    while y <> nil do
    begin
      if y^.bf = 0 then
      begin // Przypadek nr 1
        if y^.left = z then
          y^.bf := -1
        else
          y^.bf := 1;
        break;
      end
      else
      begin
        if((y^.bf = 1) and (y^.left = z)) or
          ((y^.bf = -1) and (y^.right = z)) then
        begin // Przypadek nr 2
          y^.bf := 0;
          z := y;
          y := y^.up;
        end
        else
        begin
          if y^.left = z then
            t := y^.right
          else
            t := y^.left;
          if t^.bf = 0 then
          begin // Przypadek 3A
            if y^.bf = 1 then
              rot_ll(root, y)
            else
              rot_rr(root, y);
            break;
          end
          else
            if y^.bf = t^.bf then
            begin // Przypadek 3B
              if y^.bf = 1 then
                rot_ll(root, y)
              else
                rot_rr(root, y);
              z := t;
              y := t^.up;
            end
            else
            begin // Przypadek 3C
              if y^.bf = 1 then
                rot_lr(root, y)
              else
                rot_rl(root, y);
              z := y^.up;
              y := z^.up;
            end
        end
      end
    end
  end;
  avl_remove := x;
end;

//**********************
//*** PROGRAM GŁÓWNY ***
//**********************

var
  // tablica kluczy węzłów
  Tk : array [0..31] of integer;
  i, x, i1, i2  : integer;
  // korzeń drzewa AVL
  root : PAVLnode;

begin
  // ustawiamy łańcuchy znakowe, 
  // ponieważ nie wszystkie
  // edytory pozwalają wstawiać
  // znaki konsoli do tworzenia
  // ramek.

  // cr = +--
  //      |

  // cl = |
  //      +--

  // cp = |
  //      |

  cr := #218#196;
  cl := #192#196;
  cp := #179#32;

  randomize;
  // Tworzymy puste drzewo AVL
  root := nil;
  // Tablicę wypełniamy wartościami kluczy
  for i := 0 to 31 do
    Tk[i] := i+1;
  // Mieszamy tablicę
  for i := 1 to 300 do
  begin
    // Losujemy 2 indeksy
    i1 := random(32);
    i2 := random(32);
    // Wymieniamy Tk[i1] <--> Tk[i2]
    x := Tk[i1];
    Tk[i1] := Tk[i2];
    Tk[i2] := x;
  end;
  // Na podstawie tablicy
  // tworzymy drzewo AVL
  for i := 0 to 31 do
  begin
    write(Tk[i], ' ');
    avl_insert(root, Tk [i]);
  end;
  writeln; writeln;
  // Wyświetlamy zawartość drzewa AVL
  print_bt('', '', root);
  writeln; writeln;
  // Ponownie mieszamy tablicę
  for i := 1 to 300 do
  begin
    i1 := random(32);
    i2 := random(32);
    x := Tk[i1];
    Tk[i1] := Tk[i2];
    Tk[i2] := x;
  end;
  // Usuwamy 15 węzłów
  for i := 0 to 14 do
  begin
    write(Tk[i], ' ');
    avl_remove(root, avl_find(root, Tk[i]));
  end;
  writeln; writeln;
  // Wyświetlamy zawartość drzewa AVL
  print_bt('', '', root);
  // Usuwamy drzewo AVL z pamięci
  dfs_release(root);
end.
C++
// Drzewo AVL
// Data: 22.05.2013
// (C)2013 mgr Jerzy Wałaszek
//---------------------------

#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>

using namespace std;

// Definicja typu węzłów drzewa AVL
//---------------------------------
struct AVLnode
{
  AVLnode * up, *left, *right;
  int key, bf;
};

// Zmienne globalne
//-----------------

// łańcuchy do znaków ramek
string cr, cl, cp;

// Procedura wypisuje drzewo
//--------------------------
void print_bt(string sp, 
             string sn, 
             AVLnode * v)
{
  string s;

  if(v)
  {
    s = sp;
    if(sn == cr)
      s[s.length()-2] = ' ';
    print_bt (s+cp, cr, v->right);
    s = s.substr(0, sp.length()-2);
    cout << s << sn << v->key
         << ":" << v->bf << endl;
    s = sp;
    if(sn == cl)
      s[s.length()-2] = ' ';
    print_bt(s+cp, cl, v->left);
  }
}

// Procedura DFS:postorder
// usuwająca drzewo
//------------------------
void dfs_release(AVLnode * v)
{
  if(v)
  {
    // usuwamy lewe poddrzewo
    dfs_release(v->left);
    // usuwamy prawe poddrzewo
    dfs_release(v->right);
    // usuwamy sam węzeł
    delete v;
  }
}

// Rotacja RR
//-----------
void rot_rr(AVLnode * & root, 
        AVLnode * A)
{
  AVLnode * B = A->right, 
          * p = A->up;

  A->right = B->left;
  if(A->right)
    A->right->up = A;

  B->left = A;
  B->up = p;
  A->up = B;

  if(p)
  {
    if(p->left == A)
      p->left = B;
    else
      p->right = B;
  }
  else root = B;

  if(B->bf == -1)
    A->bf = B->bf = 0;
  else
  {
    A->bf = -1;
    B->bf = 1;
  }
}

// Rotacja LL
//-----------
void rot_ll(AVLnode * & root, 
        AVLnode * A)
{
  AVLnode * B = A->left, 
          * p = A->up;

  A->left = B->right;
  if(A->left)
    A->left->up = A;

  B->right = A;
  B->up = p;
  A->up = B;

  if(p)
  {
    if(p->left == A)
      p->left = B;
    else
      p->right = B;
  }
  else root = B;

  if(B->bf == 1)
    A->bf = B->bf = 0;
  else
  {
    A->bf = 1;
    B->bf = -1;
  }
}

// Rotacja RL
//-----------
void rot_rl(AVLnode * & root, 
        AVLnode * A)
{
  AVLnode * B = A->right, 
          * C = B->left, 
          * p = A->up;

  B->left = C->right;
  if(B->left)
    B->left->up = B;

  A->right = C->left;
  if(A->right)
    A->right->up = A;

  C->left = A;
  C->right = B;
  A->up = B->up = C;
  C->up = p;

  if(p)
  {
    if(p->left == A)
      p->left = C;
    else
      p->right = C;
  }
  else root = C;

  if(C->bf == -1)
    A->bf = 1;
  else
    A->bf = 0;

  if(C->bf == 1)
    B->bf = -1;
  else
    B->bf = 0;

  C->bf = 0;
}

// Rotacja LR
//-----------
void rot_lr(AVLnode * & root, 
        AVLnode * A)
{
  AVLnode * B = A->left, 
          * C = B->right, 
          * p = A->up;

  B->right = C->left;
  if(B->right)
    B->right->up = B;

  A->left = C->right;
  if(A->left)
    A->left->up = A;

  C->right = A;
  C->left = B;
  A->up = B->up = C;
  C->up = p;

  if(p)
  {
    if(p->left == A)
      p->left = C;
    else
      p->right = C;
  }
  else root = C;

  if(C->bf == 1)
    A->bf = -1;
  else
    A->bf = 0;

  if(C->bf == -1)
    B->bf = 1;
  else
    B->bf = 0;

  C->bf = 0;
}

// Wstawia nowy węzeł do drzewa AVL
// root - referencja korzenia
// k    - klucz nowego węzła
//---------------------------------
void avl_insert(AVLnode * & root, 
               int k)
{
  AVLnode * w, * p, * r;
  bool t;

  // tworzymy dynamicznie nowy węzeł
  w = new AVLnode;
  w->left = w->right = w->up = NULL;
  w->key = k;
  w->bf = 0;

  //----------------------------------------
  // FAZA 1 - wstawienie węzła do drzewa AVL
  //----------------------------------------

  // rozpoczynamy od korzenia
  p = root;

  // jeśli drzewo jest puste, 
  // to węzeł w umieszczamy w korzeniu
  if(!p) root = w;
  else
  {
    // inaczej szukamy miejsce dla w
    while(true)
      // porównujemy klucze
      if(k < p->key)
      {
        // jeśli p nie posiada
        // lewego syna, to
        if(!p->left)
        {
          // lewym synem p
          // staje się węzeł w
          p->left = w;
          break; // wychodzimy z pętli
        }
        // inaczej przechodzimy
        // do lewego syna 
        p = p->left;
      }
      else
      {
        // jeśli p nie posiada
        // prawego syna, to
        if(!p->right)
        {
          // prawym synem 
          // staje się węzeł w
          p->right = w;
          break; // wychodzimy z pętli
        }
        // inaczej przechodzimy
        // do prawego syna 
        p = p->right;
      }
    w->up = p; // ojcem w jest p

    //---------------------------------
    // FAZA 2 - równoważenie drzewa AVL
    //---------------------------------

    if(p->bf)
      p->bf = 0; // UWAGA NR 1
    else
    {
      if(p->left == w) // UWAGA NR 2
        p->bf = 1;
      else
        p->bf = -1;

      // będziemy szli w górę drzewa
      // w kierunku korzenia
      // r i p wskazują ojca 
      // i syna na tej ścieżce
      r = p->up;
      t = false;
      while(r)
      {
        if(r->bf)
        {
          // ustalamy wynik pętli
          t = true;
          break; // przerywamy pętlę
        };
        // inaczej modyfikujemy r.bf
        if(r->left == p)
          r->bf = 1;
        else
          r->bf = -1;
        // przechodzimy w górę
        // na wyższy poziom
        p = r;
        r = r->up;
      }
      // jeśli r.bf = +/- 1, 
      // to musimy równoważyć drzewo
      if(t)
      {
        if(r->bf == 1)
        {
          // gałęzie się równoważą
          if(r->right == p)
            r->bf = 0;
          else if(p->bf == -1)
            rot_lr(root, r);
          else
            rot_ll(root, r);
        }
        else
        { // r.bf = -1
          // gałęzie się równoważą
          if(r->left == p)
            r->bf = 0;
          else if(p->bf == 1)
            rot_rl(root, r);
          else
            RR (root, r);
        }
      }
    }
  }
}

// Funkcja znajduje
// poprzednik węzła p
//-------------------
AVLnode * avl_pred(AVLnode * p)
{
  AVLnode * r;

  if(p)
  {
    if(p->left)
    {
      p = p->left;
      while(p->right)
        p = p->right;
    }
    else
      do
      {
        r = p;
        p = p->up;
      } while(p && p->right != r);
  }
  return p;
}

// Funkcja szuka w drzewie AVL
// węzła o zadanym kluczu.
// Jeśli go znajdzie, zwraca
// jego wskazanie. Jeżeli nie, 
// to zwraca wskazanie puste.
// Parametrami są:
// p - wskazanie korzenia drzewa AVL
// k - klucz poszukiwanego węzła
//----------------------------------
AVLnode * avl_find(AVLnode * p, 
                  int k)
{
  while(p && p->key != k)
    p = (k < p->key) ? p->left:
                       p->right;
  return p;
}

// Funkcja usuwa rekurencyjnie
// węzeł x
// root - referencja do zmiennej
//        z adresem korzenia
// x    - wskazanie usuwanego węzła
//---------------------------------
AVLnode * avl_remove(AVLnode * & root, 
                    AVLnode * x)
{
  AVLnode  * t, * y, * z;
  bool nest;

  if(x->left && x->right)
  {
    y = avl_remove(root, avl_pred(x));
    nest = false;
  }
  else
  {
    if(x->left)
    {
      y = x->left;
      x->left = NULL;
    }
    else
    {
      y = x->right;
      x->right = NULL;
    }
    x->bf = 0;
    nest = true;
  }

  if(y)
  {
    y->up = x->up;
    y->left = x->left;
    if(y->left)
      y->left->up = y;

    y->right = x->right;
    if(y->right)
      y->right->up = y;

    y->bf = x->bf;
  }

  if(x->up)
  {
    if(x->up->left == x)
      x->up->left = y;
    else
      x->up->right = y;
  }
  else root = y;

  if(nest)
  {
    z = y;
    y = x->up;
    while(y)
    {
      if(!y->bf)
      { // Przypadek nr 1
        if(y->left == z)
          y->bf = -1;
        else
          y->bf = 1;
        break;
      }
      else
      {
        if(((y->bf ==  1) && (y->left  == z))||
           ((y->bf == -1) && (y->right == z)))
        { // Przypadek nr 2
          y->bf = 0;
          z = y;
          y = y->up;
        }
        else
        {
          if(y->left == z)
            t = y->right;
          else t = y->left;

          if(!t->bf)
          { // Przypadek 3A
            if(y->bf == 1)
              rot_ll(root, y);
            else
              rot_rr(root, y);
            break;
          }
          else if(y->bf == t->bf)
          { // Przypadek 3B
            if(y->bf == 1)
              rot_ll(root, y);
            else
              rot_rr(root, y);
            z = t;
            y = t->up;
          }
          else
          { // Przypadek 3C
            if(y->bf == 1)
              rot_lr(root, y);
            else
              rot_rl(root, y);
            z = y->up;
            y = z->up;
          }
        }
      }
    }
  }
  return x;
}

//**********************
//*** PROGRAM GŁÓWNY ***
//**********************

int main()
{
  // tablica kluczy węzłów
  int Tk[32];
  int i, i1, i2;
  AVLnode * root = NULL;

  // ustawiamy łańcuchy znakowe, 
  // ponieważ nie wszystkie
  // edytory pozwalają wstawiać
  // znaki konsoli do tworzenia
  // ramek.

  // cr = +--
  //      |

  // cl = |
  //      +--

  // cp = |
  //      |

  cr = cl = cp = "  ";
  cr[0] = 218; cr[1] = 196;
  cl[0] = 192; cl[1] = 196;
  cp[0] = 179;

  srand(time(NULL));

  // Tablicę wypełniamy wartościami kluczy
  for(i = 0; i < 32; i++)
    Tk[i] = i+1;
  // Mieszamy tablicę
  for(i = 0; i < 300; i++)
  {
    // Losujemy 2 indeksy
    i1 = rand()%32;
    i2 = rand()%32;
    // Wymieniamy Tk[i1] <--> Tk[i2]
    swap(Tk[i1], Tk[i2]);
  }
  // Na podstawie tablicy
  // tworzymy drzewo AVL
  for(i = 0; i < 32; i++)
  {
    cout << Tk[i] << " ";
    avl_insert(root, Tk[i]);
  }
  cout << endl << endl;
  // Wyświetlamy zawartość drzewa AVL
  print_bt("", "", root);
  cout << endl << endl;
  // Ponownie mieszamy tablicę
  for(i = 0; i < 300; i++)
  {
    i1 = rand()%32;
    i2 = rand()%32;
    swap(Tk[i1], Tk[i2]);
  }
  // Usuwamy 15 węzłów
  for(i = 0; i < 15; i++)
  {
    cout << Tk[i] << " ";
    avl_remove(root, avl_find(root, Tk[i]));
  }
  cout << endl << endl;
  // Wyświetlamy zawartość drzewa AVL
  print_bt("", "", root);
  // Usuwamy drzewo AVL z pamięci
  dfs_release(root);
  return 0;}
Basic
' Drzewo AVL
' Data: 22.05.2013
' (C)2013 mgr Jerzy Wałaszek
'---------------------------

' Definicja typu węzłów drzewa AVL
'---------------------------------
Type AVLnode
  up As AVLnode Ptr
  Left As AVLnode Ptr
  Right As AVLnode Ptr
  key As Integer
  bf As Integer
End Type

' Zmienne globalne
'-----------------

' łańcuchy do ramek
Dim Shared As String * 2 cr, _
                         cl, _
                         cp

' Procedura wypisuje drzewo
'--------------------------
Sub print_bt(sp As String, _
             sn As String, _
              v As AVLnode Ptr)

  Dim As String s

  If v Then
    s = sp
    If sn = cr Then _
      Mid(s, Len(s)-1, 1) = " "
    print_bt(s+cp, cr, v->Right)

    s = Mid(s, 1, Len(sp)-2)
    Print Using "&&&:&";s;sn;v->key;v->bf

    s = sp
    If sn = cl Then _
      Mid(s, Len(s)-1, 1) = " "
    print_bt(s+cp, cl, v->Left)
  End If
End Sub

' Procedura DFS:postorder
' usuwająca drzewo
'------------------------
Sub dfs_release(v As AVLnode Ptr)
  If v Then
    ' usuwamy lewe poddrzewo
    dfs_release(v->left)
    ' usuwamy prawe poddrzewo
    dfs_release (v->right)
    ' usuwamy sam węzeł
    delete v
  End If
End Sub

' Rotacja RR
'-----------
Sub rot_rr(ByRef root As AVLnode Ptr, _
       ByVal A As AVLnode Ptr)
  Dim As AVLnode Ptr B = A->right, _
                      p = A->up
  A->right = B->Left
  If A->Right Then _
    A->right->up = A

  B->left = A
  B->up = p
  A->up = B

  If p Then
    if p->left = A Then
    	p->left = B
    Else
    	p->right = B
    End If
  Else
    root = B
  End If

  If B->bf = -1 Then
    A->bf = 0
    B->bf = 0
  Else
    A->bf = -1
    B->bf = 1
  End If
End Sub

' Rotacja LL
'-----------
Sub rot_ll(ByRef root As avlNode Ptr, _
       ByVal A As AVLnode Ptr)
  Dim As AVLnode Ptr B = A->left, _
                     p = A->up
  A->left = B->Right
  If A->left Then _
    A->left->up = A

  B->right = A
  B->up = p
  A->up = B

  If p Then
    If p->left = A Then
    	p->left = B
    Else
    	p->right = B
    End If
  Else
    root = B
  End If

  if B->bf = 1 Then
    A->bf = 0
    B->bf = 0
  Else
    A->bf =  1
    B->bf = -1
  End If
End Sub

' Rotacja RL
'-----------
Sub rot_rl(ByRef root As AVLnode Ptr, _
       ByVal A As AVLnode Ptr)
  Dim As AVLnode Ptr B = A->right, _
                     C = B->left, _
                     p = A->up
  B->left = C->Right
  if B->left Then _
    B->left->up = B

  A->right = C->Left
  If A->Right Then _
    A->right->up = A

  C->left = A
  C->right = B
  A->up = C
  B->up = C
  C->up = p

  If p Then
    if p->left = A Then
      p->left = C
    Else
      p->right = C
    End If
  Else
    root = C
  End If

  If C->bf = -1 Then
    A->bf = 1
  Else
    A->bf = 0
  End If
  
  If C->bf =  1 Then
    B->bf = -1
  Else
    B->bf = 0
  End If
  
  C->bf = 0
End Sub

' Rotacja LR
'-----------
sub rot_lr(ByRef root As AVLnode Ptr, _
       ByVal A As AVLnode Ptr)
  Dim As AVLnode Ptr B = A->left, _
                     C = B->right, _
                     p = A->up
  B->right = C->Left
  If B->right Then _
    B->right->up = B

  A->left = C->Right
  if A->left Then _
    A->left->up = A

  C->right = A
  C->left = B
  A->up = C
  B->up = C
  C->up = p

  if p Then
    If p->left = A Then
      p->left = C
    Else
      p->right = C
    End If
  Else
    root = C
  End If

  If C->bf = 1 Then
    A->bf = -1
  Else
    A->bf = 0
  End If
  
  If C->bf = -1 Then
    B->bf =  1
  Else
    B->bf = 0
  End If

  C->bf = 0
End Sub

' Wstawia nowy węzeł do drzewa AVL
' root - referencja korzenia
' k    - klucz nowego węzła
'---------------------------------
Sub avl_insert(ByRef root As AVLnode Ptr, _
                       k As Integer)	
  Dim As AVLnode Ptr w, p, r
  Dim As Integer t

  ' tworzymy dynamicznie nowy węzeł
  w = new AVLnode
  w->left  = 0
  w->right = 0
  w->up    = 0
  w->key   = k
  w->bf    = 0

  '----------------------------------------
  ' FAZA 1 - wstawienie węzła do drzewa AVL
  '----------------------------------------

  ' rozpoczynamy od korzenia
  p = root

  If p = 0 Then
    ' jeśli drzewo jest puste, 
    ' to węzeł w umieszczamy w korzeniu
    root = w         
  Else
    ' inaczej szukamy miejsce dla w
    While 1
      ' porównujemy klucze
      If k < p->key Then
        ' jeśli p nie posiada
        ' lewego syna, to
        If p->Left = 0 Then
          ' lewym synem p
          ' staje się węzeł w
          p->left = w
          Exit While ' wychodzimy z pętli
        End If
        ' inaczej przechodzimy
        ' do lewego syna 
        p = p->Left
      Else
        ' jeśli p nie posiada
        ' prawego syna, to
        If p->Right = 0 Then
          ' prawym synem 
          ' staje się węzeł w
          p->right = w
          Exit While ' wychodzimy z pętli
        End If
        ' inaczej przechodzimy
        ' do prawego syna 
        p = p->Right
      End If
    Wend
    w->up = p ' ojcem w jest p

    '---------------------------------
    ' FAZA 2 - równoważenie drzewa AVL
    '---------------------------------

    If p->bf Then
    	p->bf = 0 ' UWAGA NR 1
    Else
      ' UWAGA NR 2
      If p->left = w Then
        p->bf = 1
      Else
        p->bf = -1
      End If

      ' będziemy szli w górę drzewa
      ' w kierunku korzenia
      ' r i p wskazują ojca 
      ' i syna na tej ścieżce
      r = p->up

      t = 0
      While r
        If r->bf Then
          ' ustalamy wynik pętli
          t = 1
          Exit While ' przerywamy pętlę
        End If
        ' inaczej modyfikujemy r.bf
        If r->left = p Then
          r->bf = 1
        Else
          r->bf = -1
        End If

        ' przechodzimy w górę
        ' na wyższy poziom
        p = r
        r = r->up
      Wend
      ' jeśli r.bf = +/- 1, 
      ' to musimy równoważyć drzewo
      If t = 1 Then
        If r->bf = 1 Then
          If r->right = p Then
          	' gałęzie się równoważą
            r->bf = 0
          ElseIf p->bf = -1 Then
            rot_lr(root, r)
          Else
            rot_ll(root, r)
          End If
        Else ' r.bf = -1
          If r->left = p Then
            ' gałęzie się równoważą
            r->bf = 0
          ElseIf p->bf = 1 Then
            rot_rl(root, r)
          Else
            rot_rr(root, r)
          End If
        End If
      End If
    End If
  End If
End Sub

' Funkcja znajduje poprzednik węzła p
'------------------------------------
Function avl_pred(ByVal p As AVLnode Ptr) _
                         As AVLnode Ptr
  Dim As AVLnode Ptr r

  If p Then
    If p->Left Then
      p = p->Left
      While p->Right
      	p = p->Right
      Wend
    Else
      Do
        r = p
        p = p->up
      Loop Until p = 0 OrElse _
                 p->Right = r
    End If
  End If
  Return p
 
End Function

' Funkcja szuka w drzewie AVL
' węzła o zadanym kluczu.
' Jeśli go znajdzie, zwraca
' jego wskazanie. Jeżeli nie, 
' to zwraca wskazanie puste.
' Parametrami są:
' p - wskazanie korzenia drzewa AVL
' k - klucz poszukiwanego węzła
'----------------------------------
Function avl_find(p As AVLnode Ptr, _
                 k As Integer) _
                   As AVLnode Ptr
  while(p <> 0) AndAlso _
       (p->key <> k)
    If k < p->key Then
      p = p->Left
    Else
      p = p->Right
    End If
  Wend
  Return p
 
End Function

' Funkcja usuwa rekurencyjnie węzeł x
' root - referencja do zmiennej
'        z adresem korzenia
' x    - wskazanie usuwanego węzła
'------------------------------------
Function avl_remove(ByRef root As AVLnode Ptr, _
                            x As AVLnode Ptr) _
                              As AVLnode Ptr

  Dim As AVLnode Ptr t, y, z
  Dim As Integer nest

  if(x->Left <> 0) And _
    (x->Right <> 0) Then
    y = avl_remove(root, avl_pred(x))
    nest = 0
  Else
    If x->Left Then
      y = x->left
      x->left = 0
    Else
      y = x->right
      x->right = 0
    End If
    x->bf = 0
    nest  = 1
  End If

  If y Then
    y->up = x->up
    y->left = x->left
    If y->Left Then _
      y->left->up  = y
      
    y->right = x->right
    If y->Right Then _
      y->right->up = y
    
    y->bf = x->bf
  End If

  If x->up Then
    If x->up->left = x Then
      x->up->left = y
    Else
      x->up->right = y
    End If
  Else
    root = y
  End If

  If nest = 1 Then
    z = y
    y = x->up
    While y
      If y->bf = 0 Then ' Przypadek nr 1
        If y->left = z Then
          y->bf = -1
        Else
          y->bf = 1
        End If
        Exit While
      Else
        ' Przypadek nr 2
        if((y->bf = 1) AndAlso (y->left = z)) OrElse _
          ((y->bf = -1) AndAlso (y->right = z)) Then
          y->bf = 0
          z = y: y = y->up
        Else
          If y->left = z Then
            t = y->right
          Else
            t = y->Left
          End If
          If t->bf = 0 then ' Przypadek 3A
            If y->bf = 1 Then
              rot_ll(root, y)
            Else
              rot_rr(root, y)
            End If
            Exit While
          ElseIf y->bf = t->bf Then ' Przypadek 3B
            If y->bf = 1 Then
              rot_ll(root, y)
            Else
              rot_rr(root, y)
            End If
            z = t
            y = t->up
          Else ' Przypadek 3C
            If y->bf = 1 Then
              rot_lr(root, y)
            Else
              rot_rl(root, y)
            End If
            z = y->up
            y = z->up
          End If
        End If
      End If
    Wend
  End If
  Return x
End Function

' **********************
' *** PROGRAM GŁÓWNY ***
' **********************

' tablica kluczy węzłów
Dim As Integer Tk(31)
Dim As Integer i, i1, i2
Dim As AVLnode Ptr root = 0

' ustawiamy łańcuchy znakowe, 
' ponieważ nie wszystkie edytory
' pozwalają wstawiać znaki konsoli
' do tworzenia ramek.

' cr = +--
'      |

' cl = |
'      +--

' cp = |
'      |

cr = Chr(218)+Chr(196)
cl = Chr(192)+Chr(196)
cp = Chr(179)+" "

Randomize
' Tablicę wypełniamy wartościami kluczy
For i = 0 To 31
  Tk(i) = i+1
Next
' Mieszamy tablicę
For i = 1 To 300
  ' Losujemy 2 indeksy
  i1 = Int(Rnd()*32)
  i2 = Int(Rnd()*32)
  ' Wymieniamy Tk[i1] <--> Tk[i2]
  Swap Tk(i1), Tk(i2)
Next
' Na podstawie tablicy tworzymy drzewo AVL
For i = 0 To 31
  Print Tk(i);
  avl_insert(root, Tk(i))
Next
Print: Print
' Wyświetlamy zawartość drzewa AVL
print_bt("", "", root)
Print: Print
' Ponownie mieszamy tablicę
For i = 1 To 300
  i1 = Int(Rnd()*32)
  i2 = Int(Rnd()*32)
  Swap Tk(i1), Tk(i2)
Next
' Usuwamy 15 węzłów
For i = 0 To 14
  Print Tk(i);
  avl_remove(root, avl_find(root, Tk(i)))
Next
Print: Print
' Wyświetlamy zawartość drzewa AVL
print_bt("", "", root)
' Usuwamy drzewo BST z pamięci
dfs_release(root)
End
Python (dodatek)
# Drzewo AVL
# Data: 29.07.2024
# (C)2024 mgr Jerzy Wałaszek
#---------------------------

import random


# klasa węzłów drzewa
class AVLnode:


    def __init__(self):
        self.up    = None
        self.left  = None
        self.right = None
        self.key   = 0
        self.bf    = 0


# zmienne globalne

# wskazanie korzenia drzewa
root = None
# łańcuchy do ramek
cr = "┌─"
cl = "└─"
cp = "│ "


# procedura wypisuje drzewo
def print_bt(sp, sn, v):
    global cr, cl, cp
    s = ""
    if v:
        s = sp
        if sn == cr:
            s = s[:-2]+' '+s[-1]
        print_bt(s+cp, cr, v.right)
        s = s[:-2]
        print(s, sn, v.key, ":", v.bf, sep="")
        s = sp
        if sn == cl:
            s = s[:-2]+' '+s[-1]
        print_bt(s+cp, cl, v.left)


# Procedura DFS:postorder
# usuwająca drzewo
def dfs_release(v):
    if v:
        # usuwamy lewe poddrzewo
        dfs_release(v.left)
        # usuwamy prawe poddrzewo
        dfs_release(v.right)
        # usuwamy odwołania
        v.left = None
        v.right = None
        

# Rotacja RR
def rot_rr(a):
    global root
    b = a.right
    p = a.up
    a.right = b.left
    if a.right:
        a.right.up = a
    b.left = a
    b.up = p
    a.up = b
    if p:
        if p.left is a:
    	    p.left = b
        else:
    	    p.right = b
    else:
        root = b
    if b.bf == -1:
        a.bf, b.bf = 0, 0
    else:
        a.bf, b.bf = -1, 1


# Rotacja LL
def rot_ll(a):
    global root
    b = a.left
    p = a.up
    a.left = b.right
    if a.left:
        a.left.up = a
    b.right = a
    b.up = p
    a.up = b
    if p:
        if p.left is a:
    	    p.left = b
        else:
    	    p.right = b
    else:
        root = b
    if b.bf == 1:
        a.bf, b.bf = 0, 0
    else:
        a.bf, b.bf = 1, -1


# Rotacja RL
def rot_rl(a):
    global root
    b = a.right
    c = b.left
    p = a.up
    b.left = c.right
    if b.left:
        b.left.up = b
    a.right = c.left
    if a.right:
        a.right.up = a
    c.left = a
    c.right = b
    a.up = c
    b.up = c
    c.up = p
    if p:
        if p.left is a:
            p.left = c
        else:
            p.right = c
    else:
        root = c
    if c.bf == -1:
        a.bf = 1
    else:
        a.bf = 0
    if c.bf == 1:
        b.bf = -1
    else:
        b.bf = 0
    c.bf = 0


# Rotacja LR
def rot_lr(a):
    global root
    b = a.left
    c = b.right
    p = a.up
    b.right = c.left
    if b.right:
        b.right.up = b
    a.left = c.right
    if a.left:
        a.left.up = a
    c.right = a
    c.left = b
    a.up = c
    b.up = c
    c.up = p
    if p:
        if p.left is a:
            p.left = c
        else:
            p.right = c
    else:
        root = c
    if c.bf == 1:
        a.bf = -1
    else:
        a.bf = 0
    if c.bf == -1:
        b.bf =  1
    else:
        b.bf = 0
    c.bf = 0


# Wstawia nowy węzeł do drzewa AVL
# root - referencja korzenia
# k    - klucz nowego węzła
def avl_insert(k):
    global root
    # tworzymy dynamicznie nowy węzeł
    w = AVLnode()
    w.key = k

    # FAZA 1 - wstawienie węzła do drzewa AVL
    
    # rozpoczynamy od korzenia
    p = root
    if not p:
        # jeśli drzewo jest puste, 
        # to węzeł w umieszczamy w korzeniu
        root = w                 
    else:
        # inaczej szukamy miejsce dla w
        while True:
            # porównujemy klucze
            if k < p.key:
                # jeśli p nie posiada
                # lewego syna, to
                if not p.left:
                    # lewym synem p
                    # staje się węzeł w
                    p.left = w
                    break # wychodzimy z pętli
                # inaczej przechodzimy
                # do lewego syna 
                p = p.left
            else:
                # jeśli p nie posiada
                # prawego syna, to
                if not p.right:
                    # prawym synem 
                    # staje się węzeł w
                    p.right = w
                    break # wychodzimy z pętli
                # inaczej przechodzimy
                # do prawego syna 
                p = p.right
        w.up = p # ojcem w jest p

        # FAZA 2 - równoważenie drzewa AVL

        if p.bf:
            p.bf = 0 # UWAGA NR 1
        else:
            # UWAGA NR 2
            if p.left is w:
                p.bf = 1
            else:
                p.bf = -1
            # będziemy szli w górę drzewa
            # w kierunku korzenia
            # r i p wskazują ojca 
            # i syna na tej ścieżce
            r = p.up
            t = False
            while r:
                if r.bf:
                    # ustalamy wynik pętli
                    t = True
                    break # przerywamy pętlę
                # inaczej modyfikujemy r.bf
                if r.left is p:
                    r.bf = 1
                else:
                    r.bf = -1
                # przechodzimy w górę
                # na wyższy poziom
                p = r
                r = r.up
            # jeśli r.bf = +/- 1, 
            # to musimy równoważyć drzewo
            if t:
                if r.bf == 1:
                    if r.right is p:
                    	# gałęzie się równoważą
                        r.bf = 0
                    elif p.bf == -1:
                        rot_lr(r)
                    else:
                        rot_ll(r)
                else: # r.bf = -1
                    if r.left is p:
                        # gałęzie się równoważą
                        r.bf = 0
                    elif p.bf == 1:
                        rot_rl(r)
                    else:
                        rot_rr(r)


# Funkcja znajduje poprzednik węzła p
def avl_pred(p):
    if p:
        if p.left:
            p = p.left
            while p.right:
            	p = p.right
        else:
            while True:
                r = p
                p = p.up
                if not p or p.right is r:
                    break
    return p


# Funkcja szuka w drzewie AVL
# węzła o zadanym kluczu.
# Jeśli go znajdzie, zwraca
# jego wskazanie. Jeżeli nie, 
# to zwraca wskazanie puste.
# Parametrami są:
# k - klucz poszukiwanego węzła
def avl_find(k):
    global root
    p = root
    while p and (p.key != k):
        if k < p.key:
            p = p.left
        else:
            p = p.right
    return p


# Funkcja usuwa rekurencyjnie węzeł x
# x    - wskazanie usuwanego węzła
def avl_remove(x):
    global root
    if bool(x.left) and bool(x.right):
        y = avl_remove(avl_pred(x))
        nest = False
    else:
        if x.left:
            y = x.left
            x.left = None
        else:
            y = x.right
            x.right = None
        x.bf = 0
        nest = True
    if y:
        y.up = x.up
        y.left = x.left
        if y.left:
            y.left.up = y            
        y.right = x.right
        if y.right:
            y.right.up = y
        y.bf = x.bf
    if x.up:
        if x.up.left is x:
            x.up.left = y
        else:
            x.up.right = y
    else:
        root = y
    if nest:
        z = y
        y = x.up
        while y:
            if not y.bf: # Przypadek nr 1
                if y.left is z:
                    y.bf = -1
                else:
                    y.bf = 1
                break
            else:
                # Przypadek nr 2
                if((y.bf ==  1) and (y.left  is z)) or \
                  ((y.bf == -1) and (y.right is z)):
                    y.bf = 0
                    z = y
                    y = y.up
                else:
                    if y.left is z:
                        t = y.right
                    else:
                        t = y.left
                    if not t.bf: # Przypadek 3A
                        if y.bf == 1:
                            rot_ll(y)
                        else:
                            rot_rr(y)
                        break
                    elif y.bf == t.bf: # Przypadek 3B
                        if y.bf == 1:
                            rot_ll(y)
                        else:
                            rot_rr(y)
                        z = t
                        y = t.up
                    else: # Przypadek 3C
                        if y.bf == 1:
                            rot_lr(y)
                        else:
                            rot_rl(y)
                        z = y.up
                        y = z.up
    return x


# **********************
# *** PROGRAM GŁÓWNY ***
# **********************

# Tablicę wypełniamy wartościami kluczy
tk = [i+1 for i in range(32)]
# Mieszamy tablicę
for i in range(300):
    # Losujemy 2 indeksy
    i1 = random.randrange(32)
    i2 = random.randrange(32)
    # Wymieniamy tk[i1] <--> tk[i2]
    tk[i1], tk[i2] = tk[i2], tk[i1]
# Na podstawie tablicy tworzymy drzewo AVL
for i in range(32):
    print(tk[i], end=" ")
    avl_insert(tk[i])
print()
print()
# Wyświetlamy zawartość drzewa AVL
print_bt("", "", root)
print()
print()
# Ponownie ieszamy tablicę
for i in range(300):
    # Losujemy 2 indeksy
    i1 = random.randrange(32)
    i2 = random.randrange(32)
    tk[i1], tk[i2] = tk[i2], tk[i1]
# Usuwamy 15 węzłów
for i in range(15):
    print(tk[i], end=" ")
    avl_remove(avl_find(tk[i]))
print()
print()
# Wyświetlamy zawartość drzewa AVL
print_bt("", "", root)
# Usuwamy drzewo BST z pamięci
dfs_release(root)
root = None
Wynik:
25 23 17 21 4 15 3 10 29 12 14 9 7 20 18 28 11 16 6 24 5 1 2 26 22 19 30 27 8 13 31 32

        ┌─32:0
      ┌─31:-1
    ┌─30:-1
    │ └─29:0
  ┌─28:0
  │ │   ┌─27:0
  │ │ ┌─26:-1
  │ └─25:-1
  │   └─24:0
┌─23:-1
│ │   ┌─22:0
│ │ ┌─21:-1
│ └─20:0
│   │ ┌─19:0
│   └─18:-1
17:0
│     ┌─16:0
│   ┌─15:1
│   │ └─14:1
│   │   └─13:0
│ ┌─12:-1
│ │ │ ┌─11:0
│ │ └─10:-1
└─9:0
  │     ┌─8:0
  │   ┌─7:-1
  │ ┌─6:-1
  │ │ └─5:0
  └─4:-1
    │ ┌─3:0
    └─2:0
      └─1:0


21 32 23 10 16 12 17 24 7 14 31 5 19 20 29

    ┌─30:0
  ┌─28:1
  │ │ ┌─27:0
  │ └─26:-1
┌─25:-1
│ │ ┌─22:0
│ └─18:-1
15:0
│   ┌─13:1
│   │ └─11:0
│ ┌─9:-1
│ │ └─8:0
└─6:0
  │ ┌─4:1
  │ │ └─3:0
  └─2:-1
    └─1:0

do podrozdziału  do strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2026 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.