Alberi AVL

Introduzione

Gli alberi AVL sono delle speciali tipologie di alberi binari di ricerca. Il nome AVL deriva dalle iniziali dei nomi di chi gli scoprì, Adelson-Velskij e Landis.
La loro caratteristica principale è il fatto che garantiscono un’altezza h = O(log n).
Nota: per la stesura del seguente elaborato abbiamo preso ispirazione dal libro Strutture di dati e algoritmi, poiché il Cormen che utilizziamo sempre non tratta diversi particolari di cui andremo a discutere.

Descrizione

Gli alberi AVL fanno parte dell’insieme degli alberi 1-bilanciati, ciò significa che per ogni nodo u deve essere rispettata la seguente condizione:
|h(u.left) – h(u.right)| ≤ 1
In parole significa che per ogni nodo, l’altezza dei suoi due figli differiscono al più di 1. Ricordiamo che in un albero binario, l’altezza di un nodo è la distanza che c’è tra il nodo stesso e una foglia.

alberi avl

Le due proprietà di cui gli alberi AVL godono, ovvero di essere 1-bilanciati ed avere altezza logaritmica, derivano dagli alberi di Fibonacci, che sono un sottinsieme degli alberi 1-bilanciati che raggiungono altezza h col minor numero di nodi possibile, per questo sono detti minimali.
Se eliminiamo un nodo su questo albero, possono accadere due cose:
– l’altezza diminuisce
– l’albero non è più 1-bilanciato
Nessun albero 1-bilanciato con n nodi e altezza h può avere meno di nh nodi, ossia n ≥ nh.

Albero di Fibonacci

Un albero di Fibonacci, che chiameremo Fibh , è definito ricorsivamente: per h = 0 abbiamo un solo nodo n0 = 1, per h = 1 abbiamo un albero con n1 = 2 nodi, poi per h > 1, Fibh è costruito prendendo un albero Fibh-1 e un albero Fibh-2, le cui radici diventano i figli di una nuova radice.
Osservando la figura sottostante possiamo vedere meglio che l’albero di dimensione successiva è costruito a partire dai casi precedenti.
Quindi abbiamo che il numero di nodi nell’albero di Fibonacci equivale a
nh = nh-1 + nh-2 + 1
quindi dove 1 è la radice, Fibh-1 è il sotto albero destro (o sinistro) e Fibh-2 è il sotto albero sinistro (o destro).
ll nome “Fibonacci” deriva dalla similarità con la formula ricorsiva della serie di Fibonacci, e che quindi la sua dimensione cresce esponenzialmente in modo simile ai numeri di Fibonacci, che dimostreremo in seguito.
Sotto abbiamo messo una figura che rappresenta la costruzione ricorsiva di un albero di Fibonacci, i nodi colorati in verde sono quelli che vengono aggiunti ad ogni passo.

albero di Fibonacci

Dimostrazione per induzione che un AF è minimale

Proviamo adesso a dimostrare formalmente che gli alberi di Fibonacci sono 1-bilanciati minimali, ovvero che contengono il minor numero di nodi possibile. Verifichiamo quindi che se togliamo un nodo perdiamo la proprità di 1-bilanciamento o non manteniamo l’altezza h.
Dimostrazione:
Caso base
In Fib0 l’altezza vale 0, quindi eliminando un nodo l’altezza passa a -1.
In Fib1 l’altezza vale 1, quindi eliminando un nodo l’altezza passa a 0.
Ipotesi intuttiva: per ogni altezza i < h, Fibi è minimale.
Passo induttivo
Supponiamo per assurdo che Fibh non sia minimale, cioè che togliendo un nodo possiamo mantenere altezza h e mantenere la proprietà di 1-bilanciamento. Osserviamo che il nodo da togliere non può essere la radice, altrimenti non sarebbe più un albero, allora il nodo da togliere dovrà appartenere a Fibh-1 o a Fibh-2, (vedi figura precente in Fibh).
Se togliamo un nodo da Fibh-1 succede che l’altezza passa da h-1 a h-2.
Se togliamo, invece un nodo da Fibh-2 si perde la condizione di 1-bilanciamento, perché a sinistra abbiamo altezza Fibh-1, mentre a destra avremo Fibh-3, allora la radice rimane sbilanciata di 2.
Con questo abbiamo ottenuto una contraddizione che dimostra che l’albero di Fibonacci è 1-bilanciato minimale.

albero di Fibonacci
Rimozione nodo su Fibh-1
albero di Fibonacci
Rimozione nodo su Fibh-2

Facciamo adesso altre due dimostrazioni per verificare che un albero di Fibonacci (AF) ha altezza h e dimensione che cresce esponenzialmente come la serie di Fibonacci.

Dimostrazione altezza logaritmica di un albero di Fibonacci

Come anticipato, gli alberi di Fibonacci si chiamano in questo modo perché la loro dimensione cresce in modo simile ai numeri della serie di Fibonacci. La differenza è che c’è in più un +1, quindi,
nh = nh-1 + nh-2 + 1.
Se controlliamo una serie di valori di nh noteremo che vale la relazione
nh = Fh+3 – 1.
Dimostriamo allora per induzione.
Caso base
h = 0 -> n0 = 1, infatti F0+3 – 1 = 2 – 1 = 1
h = 1 -> n1 = 2, infatti F1+3 – 1 = 3 – 1 = 2

Prima di passare al passo induttivo facciamo un’osservazione.

Sappiamo che per il calcolo dell’h-esimo numero di Fibonacci esiste una formula chiusa:

formula chiusa n-esimo numero di Fibonacci
numero aureo Fibonacci
Numero Aureo

dobbiamo allora trovare un modo per calcolare anche nh.
Se guardiamo la tabulazione sottostante notiamo che nh vale 4 per h = 2 e Fh vale 5 per h = 5.
Inoltre possiamo notare anche che nh vale 7 per h = 3 e Fh vale 8 per h = 6 e così via.

Questo significa che nh corrisponde a tre posizioni h in avanti (Fh+3) e poi da quel valore togliamo 1, fatta questa osservazione possiamo dimostrare per induzione che nh = Fh+3 – 1 per ogni i < h.
Passo induttivo
Ipotesi intuttiva: per ogni altezza i < h, vale ni = Fi+3 – 1
Dato che nh = nh-1 + nh-2 + 1, allora vale che
1 + (F(h-1)+3 – 1) + (F(h-2)+3 – 1) =
1 + Fh+2 – 1 + Fh+1 – 1 =
Fh+3 – 1

Siccome Fh ≥ ch con c > 1 e h > 2, possiamo dire che nh = Fh+3 – 1 ≥ ch.
Sappiamo che nh è il numero minimo di nodi per un albero di altezza h (perché AF è minimale) e quindi se prendiamo un albero AVL qualsiasi di n nodi, sicuramente n ≥ nh ,
di conseguenza n ≥ ch, quindi trasformando questa disequazione abbiamo che h = O(log n).

Inserimento elemento

L’inserimento di un elemento in un albero AVL richiede delle operazioni aggiuntive che servono per garantire che esso assuma la corretta posizione. Per fare questo dobbiamo utilizzare le rotazioni, che spieghiamo in questo articolo -> link.
Qua sotto lasciamo lo psudocodice, che come possiamo vedere è uguale a quello di un normale albero binario di ricerca, soltanto che vengo effettuate delle operazioni aggiuntive, appunto le rotazioni, che servono per il ribilanciamento.

Insert(u, n) {

  if(u = NULL) 
    return u = NewNode(n) //funzione creazione elemento

  else if(n < u.key) {
    u.left = Insert(u.left, n)

    if( Height(u.left) - Height(u.right) == 2 ) {
      if(n > u.left.key)
        u.left = CounterclockwiseRotation(u.left)
      
      u = ClockwiseRotation(u)
    }

  } else {
    u.right = Insert(u.right, n)

    if( Height(u.right) - Height(u.left) == 2 ) {
      if(n < u.right.key) 
        u.right = CounterclockwiseRotation(u.right)
      
      u = ClockwiseRotation(u)
    }
  }

  u.height = max( Height(u.left), Height(u.right) ) + 1

  return u
}

0 0 votes
Article Rating

Hai trovato questo articolo utile?

Subscribe
Notificami
guest

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

0 Commenti
Inline Feedbacks
View all comments