Árvores AVL
1. O Problema da BST “Degenerada”
Como vimos, se inserirmos 1, 2, 3, 4 em uma BST, ela vira uma Linha Reta com altura \(N\). A busca degrada para \(O(N)\). Para resolver isso, G.M. Adelson-Velsky e E.M. Landis criaram, em 1962, a primeira árvore balanceada auto-ajustável.
2. Invariante da AVL
Uma BST é uma AVL se, para todo nó, a diferença de altura entre a subárvore esquerda e a direita (Fator de Balanceamento) é no máximo 1.
\[ FB(nó) = h(direita) - h(esquerda) \] \[ FB \in \{-1, 0, +1\} \]
Se, após uma inserção ou remoção, algum nó ficar com \(|FB| > 1\), precisamos consertá-lo.
3. Implementação: Calculando Altura
Diferente da BST comum, cada nó da AVL precisa armazenar sua própria altura para evitar recalcular recursivamente (o que seria \(O(N)\)).
class Node {
int key, height;
Node left, right;
Node(int d) {
key = d;
height = 1; // Nó novo é folha, altura 1
}
}
int height(Node N) {
if (N == null) return 0;
return N.height;
}
int getBalance(Node N) {
if (N == null) return 0;
return height(N.right) - height(N.left);
}4. As Rotações
As rotações são as operações mágicas que reduzem a altura da árvore localmente mantendo a ordem da BST (\(mq < Raiz < Mq\)).
4.1. Rotação Simples à Direita (Right Rotate)
Usada quando o desbalanceamento é na Esquerda-Esquerda (O filho esquerdo está pesado na esquerda).
graph TD
y((y)) --> x((x))
y --> T3
x --> T1
x --> T2
Vira:
graph TD
x((x)) --> T1
x --> y((y))
y --> T2
y --> T3
Código Java:
Node rightRotate(Node y) {
// Definindo variáveis
Node x = y.left;
Node T2 = x.right;
// A Rotação em si (troca de ponteiros)
x.right = y;
y.left = T2;
// Atualiza alturas (primeiro y, que desceu, depois x)
y.height = Math.max(height(y.left), height(y.right)) + 1;
x.height = Math.max(height(x.left), height(x.right)) + 1;
// Nova raiz
return x;
}4.2. Rotação Simples à Esquerda (Left Rotate)
Simétrica à anterior. Usada para desbalanceamento Direita-Direita.
Node leftRotate(Node x) {
Node y = x.right;
Node T2 = y.left;
y.left = x;
x.right = T2;
x.height = Math.max(height(x.left), height(x.right)) + 1;
y.height = Math.max(height(y.left), height(y.right)) + 1;
return y;
}4.3. Rotação Dupla
Se o filho pesado tiver o “peso” no lado interno, a rotação simples não resolve (cria um “joelho”). Precisamos de duas rotações.
Caso Esquerda-Direita: 1. Rotaciona o filho esquerdo para a esquerda (leftRotate(node.left)). 2. Rotaciona o nó atual para a direita.
Caso Direita-Esquerda: 1. Rotaciona o filho direito para a direita. 2. Rotaciona o nó atual para a esquerda.
5. Inserção Completa
Node insert(Node node, int key) {
// 1. Inserção normal de BST
if (node == null) return (new Node(key));
if (key < node.key)
node.left = insert(node.left, key);
else if (key > node.key)
node.right = insert(node.right, key);
else
return node; // Chaves duplicadas não permitidas
// 2. Atualiza altura do ancestral
node.height = 1 + Math.max(height(node.left), height(node.right));
// 3. Verifica Balanceamento
int balance = getBalance(node);
// 4. Aplica Rotações se necessário
// Caso Esquerda-Esquerda
if (balance < -1 && key < node.left.key)
return rightRotate(node);
// Caso Direita-Direita
if (balance > 1 && key > node.right.key)
return leftRotate(node);
// Caso Esquerda-Direita (Joelho)
if (balance < -1 && key > node.left.key) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// Caso Direita-Esquerda (Joelho)
if (balance > 1 && key < node.right.key) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}6. AVL vs Árvore Rubro-Negra
| Característica | AVL | Rubro-Negra (Red-Black) |
|---|---|---|
| Balanceamento | Rígido (fator \(\le 1\)). | Relaxado (caminho mais longo \(\le 2 \times\) curto). |
| Altura da Árvore | Menor (\(\approx 1.44 \log N\)). | Maior (\(\approx 2 \log N\)). |
| Busca (Lookup) | Mais rápida. | Levemente mais lenta. |
| Inserção/Remoção | Mais lenta (muitas rotações). | Mais rápida. |
| Uso Real | Bancos de dados com muita leitura. | java.util.TreeMap, C++ std::map (Geral). |
Conclusão
A AVL é excelente para cenários Read-Heavy (muitas buscas, poucas alterações). Se houver muitas escritas, a Árvore Rubro-Negra é preferível.