Árvores rubro-negras
A árvore rubro-negra convencional, suas cinco propriedades, a altura negra e a demonstração de que a altura é sempre O(log n).
A árvore AVL evita a degeneração da árvore de busca com um critério
rígido:
Esta página trata da árvore rubro-negra convencional: o que ela é, suas propriedades e por que elas garantem o balanceamento. A implementação que vamos estudar é a de uma variante mais simples, a árvore rubro-negra esquerdista, que é o assunto da próxima página.
Definição
Uma árvore rubro-negra é uma árvore binária de busca em que cada nó tem, além da chave e dos dois ponteiros, uma cor, vermelha ou preta, e que satisfaz as cinco propriedades:
- todo nó é vermelho ou preto;
- a raiz é preta;
- toda folha
NULLé preta; - se um nó é vermelho, os seus dois filhos são pretos (não há dois vermelhos seguidos em nenhum caminho);
- para cada nó, todos os caminhos desse nó até as folhas
NULLabaixo dele passam pelo mesmo número de nós pretos.
O nome vem da cor dos nós (rubro é vermelho). Em inglês, a árvore é chamada de red-black tree, e também aparece em português como árvore vermelho-preta.
As folhas NULL
A propriedade 3 trata os ponteiros NULL como se fossem folhas de verdade,
todas pretas. Elas importam na hora de contar os nós pretos de um caminho
(propriedade 5). Como todo nó sem filhos termina em dois NULL, costumamos
omiti-las nos desenhos: as duas representações abaixo são a mesma árvore.
Nas figuras desta página e da próxima, a aresta que chega a um nó vermelho também é desenhada em vermelho. Isso vai ajudar a ler a variante esquerdista.
Altura negra
A propriedade 5 dá origem a uma medida importante.
A altura negra de um nó NULL, sem contar o próprio NULL. A altura negra
de uma folha NULL é 0, e a altura negra da árvore é a da raiz.
Pela propriedade 5, esse número não depende do caminho escolhido, então
Confira as propriedades: a raiz 7 é preta; nenhum vermelho (18, 8, 11 e 26)
tem filho vermelho; e todo caminho da raiz até uma folha NULL passa por
exatamente dois nós pretos abaixo da raiz. Por exemplo, NULL
passa pelo 3 e pela folha, e NULL passa pelo 10
e pela folha.
Na árvore acima, a SAE da raiz (só o 3) tem altura 0, e a SAD (com raiz 18)
tem altura 2. O fator de balanceamento da raiz é
Na árvore da figura, o que acontece se o nó 26 for pintado de preto?
Árvores que não são rubro-negras
Cada árvore abaixo viola uma condição diferente. As folhas NULL estão
desenhadas porque fazem diferença na contagem dos pretos.
A árvore (d) lembra que a rubro-negra é, antes de tudo, uma árvore binária de busca: as cores se somam às regras de ordem, não as substituem.
Uma árvore rubro-negra pode ter apenas nós pretos? Se puder, o que a propriedade 5 diz sobre a forma dessa árvore?
Ver respostaEsconder resposta
Pode. Nenhuma propriedade exige nós vermelhos. Mas, sem vermelhos, o
número de nós pretos de um caminho é o número de nós do caminho, e a
propriedade 5 passa a exigir que todos os caminhos da raiz até uma
folha NULL tenham o mesmo comprimento. Ou seja, todas as folhas estão no
mesmo nível: a árvore é cheia. Os
nós vermelhos são o que dá flexibilidade à rubro-negra para ter qualquer
número de nós.
Por que as cores garantem o balanceamento
As propriedades 4 e 5, juntas, limitam o quanto um caminho pode ser mais
longo que outro. Na figura, o caminho mais curto até uma folha NULL só
passa por nós pretos, e o mais longo alterna pretos e vermelhos.
Em uma árvore rubro-negra, nenhum caminho da raiz até uma folha NULL tem
mais que o dobro de arestas de outro.
Demonstraçãotente antes de abrir
Seja NULL passa, abaixo da raiz, por exatamente NULL. Então há no máximo um vermelho para cada preto, e o
caminho tem entre
Daqui sai a altura logarítmica. Primeiro, mostramos que uma altura negra grande obriga a árvore a ter muitos nós.
A subárvore com raiz em um nó NULL).
Demonstraçãotente antes de abrir
Por indução na altura de NULL, então
Se NULL)
satisfaz
Uma árvore rubro-negra com
Em particular,
Demonstraçãotente antes de abrir
Considere o nó mais fundo da árvore, na profundidade NULL. O caminho da raiz até essa folha tem NULL, que é
preta. Como na Proposição 1, cada vermelho desse caminho é
seguido por um preto, então pelo menos metade desses
isto é,
Compare com o teorema da altura da AVL, que dá cerca de
Inserção e remoção: manter as propriedades
As operações de busca, mínimo, máximo e percurso em ordem são exatamente as da ABB: as cores não interferem nelas. Já a inserção e a remoção precisam terminar com uma árvore que ainda satisfaça as cinco propriedades.
Inserção
A inserção começa como na ABB: o novo nó entra no lugar de um NULL. A
pergunta é que cor ele deve receber.
Um nó novo preto acrescentaria um preto apenas aos caminhos que passam por ele, violando a propriedade 5 em todos os ancestrais, um problema espalhado pela árvore inteira. Um nó novo vermelho não muda nenhuma altura negra. No máximo, ele viola a propriedade 2 (se for a raiz, basta pintá-lo de preto) ou a propriedade 4 (se o pai também for vermelho). Esse segundo problema é local, e é corrigido olhando o tio do novo nó (o irmão do pai):
- se o tio é vermelho, basta recolorir: o pai e o tio ficam pretos e o avô fica vermelho. As alturas negras não mudam, mas o avô vermelho pode ter pai vermelho, e o problema sobe dois níveis;
- se o tio é preto, uma ou duas rotações, com uma recoloração, resolvem o problema de vez.
Os dois casos têm versões espelhadas (com o pai à direita do avô), e o caso do tio preto tem uma variante com rotação dupla, como na AVL. A inserção faz no máximo duas rotações, mas a implementação precisa tratar todos esses casos.
Remoção
Remover um nó vermelho do fundo da árvore é inofensivo: nenhum caminho perde um preto. O problema é remover um nó preto.
Os caminhos que passavam pelo nó removido ficam com um preto a menos, e a propriedade 5 deixa de valer. A remoção convencional compensa essa falta com recolorações e até três rotações, em vários casos e seus espelhos. É a operação mais trabalhosa da árvore, e é por isso que ela é a grande vantagem da variante da próxima página: na rubro-negra esquerdista, a remoção é feita com as mesmas poucas operações da inserção, e o nó removido é sempre vermelho.
A rubro-negra convencional é a árvore balanceada mais usada na prática: o
std::map e o std::set do C++, o TreeMap do Java e várias estruturas do
núcleo do Linux são árvores rubro-negras. O motivo é o número pequeno e
limitado de rotações por operação.
Referências
- T. H. Cormen, C. E. Leiserson, R. L. Rivest e C. Stein. Algoritmos: Teoria e Prática. 3ª ed. Elsevier, 2012. Capítulo 13.
- A. Backes. Estrutura de Dados Descomplicada em Linguagem C. Elsevier, 2016.
- R. Sedgewick e K. Wayne. Algorithms. 4ª ed. Addison-Wesley, 2011. Seção 3.3.
- D. Galles. Visualização interativa da árvore rubro-negra. University of San Francisco.
Árvores AVL
A árvore AVL, uma árvore de busca que guarda a altura em cada nó e usa rotações para manter |fb| ≤ 1 em todos os nós, com altura sempre O(log n).
Rubro-negra esquerdista (LLRB)
A árvore rubro-negra esquerdista (LLRB) em C, com rotações, inversão de cores, inserção, as operações auxiliares da remoção, a remoção do menor e a remoção de uma chave qualquer.