Menu
Coddy logo textTech

Albero AVL

Ultimo aggiornamento

Un albero AVL è un albero binario di ricerca autobilanciato. Funziona come un normale BST, ma dopo ogni inserimento controlla il fattore di bilanciamento di ogni antenato, cioè la differenza di altezza tra il sottoalbero sinistro e quello destro. Se un nodo diventa sbilanciato (differenza maggiore di 1), esegue delle rotazioni per ripristinare l'equilibrio. Premi play qui sopra per vedere i valori inseriti e l'albero che ruota per tornare in forma.

Dato che non lascia mai l'albero pendere più di poco da un lato, un albero AVL garantisce un'altezza O(log n), quindi ricerca, inserimento ed eliminazione sono sempre O(log n), anche con un input ordinato che rovinerebbe un BST semplice. Il costo sono le rotazioni extra e la gestione delle altezze a ogni inserimento.

Complessità temporale e spaziale

OperazioneComplessitàNote
RicercaO(log n)L'altezza è sempre circa 1,44 log n
InserimentoO(log n)Più O(1) rotazioni
EliminazioneO(log n)Più O(log n) rotazioni
SpazioO(n)Un campo altezza per nodo

I quattro casi di rotazione

CasoSbilanciamentoCorrezione
Sinistra-SinistraPesante a sinistra del figlio sinistroUna rotazione a destra
Destra-DestraPesante a destra del figlio destroUna rotazione a sinistra
Sinistra-DestraPesante a destra del figlio sinistroRotazione a sinistra, poi a destra
Destra-SinistraPesante a sinistra del figlio destroRotazione a destra, poi a sinistra

Esempio svolto

Inserimento di [10, 20, 30, 40, 50, 25] un valore alla volta:

PassoStrutturaAzione
Inserisci 1010Il primo nodo diventa la radice; bilanciato
Inserisci 2010(_, 20)Va a destra di 10; ancora bilanciato
Inserisci 3020(10, 30)Caso Destra-Destra in 10, quindi una rotazione a sinistra porta 20 alla radice
Inserisci 4020(10, 30(_, 40))Va a destra di 30; ogni fattore di bilanciamento resta entro ±1
Inserisci 5020(10, 40(30, 50))Caso Destra-Destra in 30, quindi una rotazione a sinistra in 30 fa salire 40
Inserisci 2530(20(10, 25), 40(_, 50))Caso Destra-Sinistra in 20: ruota a destra il sottoalbero di 40, poi ruota a sinistra 20

Quando usare un albero AVL

Usalo quandoEvitalo quando
Le ricerche superano di gran lunga gli inserimenti e vuoi l'altezza più bassa possibileDominano inserimenti ed eliminazioni: le rotazioni e i ribilanciamenti extra costano più di quelli di un albero rosso-nero
Ti serve un caso peggiore O(log n) garantito, anche con input ostili o ordinatiBasterebbe una semplice tabella hash: non ti servono visite ordinate né query su intervalli
Ti servono operazioni ordinate: visita in ordine, predecessore/successore, query su intervalliL'insieme di dati è minuscolo: un BST semplice o un array ordinato è più semplice e abbastanza veloce
I dati possono arrivare già ordinati, cosa che trasformerebbe un BST non bilanciato in una lista concatenataNon puoi permetterti la memoria extra del campo altezza per nodo in un ambiente molto limitato

Codice AVL Tree

Un'implementazione di AVL Tree pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice AVL Tree in Python

Python
1class Node:2    def __init__(self, key):3        self.key = key4        self.left = None5        self.right = None6        self.height = 17
8
9def height(node):10    return node.height if node else 011
12
13def update(node):14    node.height = 1 + max(height(node.left), height(node.right))15
16
17def rotate_right(y):18    x = y.left19    y.left = x.right20    x.right = y21    update(y)22    update(x)23    return x24
25
26def rotate_left(x):27    y = x.right28    x.right = y.left29    y.left = x30    update(x)31    update(y)32    return y33
34
35def insert(node, key):36    if node is None:37        return Node(key)38    if key < node.key:39        node.left = insert(node.left, key)40    else:41        node.right = insert(node.right, key)42    update(node)43    balance = height(node.left) - height(node.right)44    # Four imbalance cases: LL, RR, LR, RL45    if balance > 1 and key < node.left.key:46        return rotate_right(node)47    if balance < -1 and key > node.right.key:48        return rotate_left(node)49    if balance > 1:50        node.left = rotate_left(node.left)51        return rotate_right(node)52    if balance < -1:53        node.right = rotate_right(node.right)54        return rotate_left(node)55    return node56
57
58def inorder(node):59    if node is None:60        return []61    return inorder(node.left) + [node.key] + inorder(node.right)62
63
64root = None65for key in [10, 20, 30, 40, 50, 25]:66    root = insert(root, key)67
68print("Inorder:", inorder(root))69print("Root:", root.key, "| tree height:", root.height)
Esegui questo codice nel playground Python

Domande frequenti sull'albero AVL

Cos'è il fattore di bilanciamento in un albero AVL?
Il fattore di bilanciamento di un nodo è l'altezza del suo sottoalbero sinistro meno l'altezza del sottoalbero destro. Un albero AVL mantiene il fattore di ogni nodo a -1, 0 o +1; se un inserimento lo porta fuori da questo intervallo, una rotazione ripristina l'equilibrio.
Qual è la differenza tra un albero AVL e un albero rosso-nero?
Entrambi sono BST autobilanciati con operazioni O(log n). Gli alberi AVL sono bilanciati in modo più rigido, quindi le ricerche sono leggermente più veloci, ma possono fare più rotazioni negli inserimenti e nelle eliminazioni. Gli alberi rosso-neri sono bilanciati in modo più lasco e fanno meno rotazioni: per questo molte librerie standard li usano per mappe e insiemi.
Perché usare un albero AVL invece di un semplice albero binario di ricerca?
Un BST semplice può degradare a operazioni O(n) se i valori arrivano in ordine, formando una catena sbilanciata. Un albero AVL ruota per restare bilanciato e garantisce altezza e operazioni O(log n) qualunque sia l'ordine di inserimento.
Per l'indice di un database è meglio un albero AVL o un albero rosso-nero?
Dipende dal carico di lavoro. Gli alberi AVL sono bilanciati in modo più rigido, quindi vincono quando dominano le letture e vuoi i percorsi di ricerca più corti possibili. Però la maggior parte dei database e delle librerie dei linguaggi sceglie gli alberi rosso-neri (o i B-tree), perché ribilanciano con meno rotazioni nei carichi con molte scritture, e questo conta di più quando inserimenti ed eliminazioni sono frequenti.
Quante rotazioni servono per un singolo inserimento in un AVL?
Al massimo una rotazione, semplice oppure doppia (in due passaggi), basta per ribilanciare l'albero dopo l'inserimento di un valore. Questo perché un inserimento aumenta l'altezza di un sottoalbero al massimo di uno, quindi basta una sola correzione sull'antenato sbilanciato più in basso. L'eliminazione è diversa: può richiedere fino a O(log n) rotazioni, perché il ribilanciamento si propaga verso la radice.
Devo aggiornare le altezze dei nodi dopo ogni rotazione?
Sì. Un errore comune è ruotare correttamente i puntatori ma dimenticare di ricalcolare l'altezza dei due nodi coinvolti. Dopo ogni rotazione devi aggiornare prima l'altezza del nodo che scende e poi quella del nodo che sale, perché l'altezza del nodo promosso dipende dai suoi nuovi figli. Se salti questo passaggio, i fattori di bilanciamento restano vecchi e rovinano le decisioni di ribilanciamento successive.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA