Lista de exercícios
Aula prática 10: árvores AVL
8 exercícios sobre fator de balanceamento, quatro rotações e manutenção de AVL, com traces em C e soluções ilustradas.
Esta lista retoma a ABB da prática 9 e exercita o conteúdo da aula de AVL. Use Resposta esperada para comparação e autocorreção, sem entrega ou correção individual. Faça os desenhos no papel ou na ferramenta que preferir. Os exercícios com código pedem trace manual, sem implementar um programa. O tempo estimado inclui tentativa e conferência.
Convenções: os exercícios são independentes. A altura conta níveis: árvore vazia tem altura 0 e folha tem altura 1. O fator de balanceamento é FB = altura da esquerda − altura da direita. Uma AVL mantém a ordem da ABB e FB em {-1, 0, 1} em todos os nós. Isso não exige uma árvore completa nem a menor altura possível.
Cada no_avl guarda chave, altura e os ponteiros de filhos esquerda e direita. O tipo arvore_avl é um ponteiro para nó:
/* Cada nó guarda a chave, a altura da subárvore e os endereços dos dois filhos. */
typedef struct no_avl {
int chave;
int altura;
struct no_avl *esquerda;
struct no_avl *direita;
} no_avl;
/* O tipo representa um endereço de nó. arvore_avl* aponta para a caixa da raiz. */
typedef no_avl *arvore_avl;
/* Aloca apenas a caixa da raiz e cria uma árvore vazia, como na ABB da aula 9. */
static arvore_avl *criar_avl(void) {
arvore_avl *arvore = malloc(sizeof(*arvore));
/* A caixa só pode receber uma raiz vazia quando sua alocação existe. */
if (arvore == NULL) {
return NULL;
}
*arvore = NULL;
return arvore;
}
Como na prática 9, arvore_avl *arvore = criar_avl(); aloca a caixa da raiz. arvore aponta para a caixa, *arvore guarda o endereço do primeiro nó e (*arvore)->chave acessa sua chave. arvore == NULL indica falha de criação. Uma caixa válida com *arvore == NULL representa a árvore vazia. Inserção, busca e remoção recebem arvore, como em inserir_avl(arvore, 5).
Nos exercícios de rotação, no_avl *raiz aponta diretamente para a subárvore tratada. A caixa permanece no mesmo endereço durante inserções e remoções, mesmo quando seu conteúdo muda. Ao encerrar, liberar_avl(arvore) libera os nós e a caixa. Depois, o chamador faz arvore = NULL.
h nas figuras representa altura. NULL indica ausência de filho. Os trechos pressupõem <stdlib.h> e <stdbool.h>. bool usa true e false. O operador condição ? valor1 : valor2 escolhe um dos dois valores. Por exemplo, no == NULL ? 0 : no->altura retorna 0 para uma subárvore vazia. const indica um acesso somente para consulta.
Exercício 1: calcular o FB e verificar a árvore inteira
Observe as ligações. As chaves já obedecem à propriedade da ABB.
FB = altura da subárvore esquerda − altura da subárvore direita.
O nível do nó indica sua posição a partir da raiz da árvore, que está no nível 1. A altura da subárvore é a quantidade de nós no maior caminho de sua própria raiz até uma folha. Uma folha tem altura 1 e uma subárvore vazia tem altura 0. Calcule das folhas para os pais: altura do pai = 1 + maior altura entre suas subárvores esquerda e direita.
Calcule a altura e o FB de cada nó. A árvore é AVL? Justifique com os fatores encontrados.
Solução
Explicação
Comece pelas folhas, com altura 1 e FB 0. Depois calcule cada pai usando as alturas dos filhos. Um filho ausente contribui com altura 0.
Na raiz, as duas subárvores têm altura 3. Ainda assim, é necessário verificar seus descendentes. O critério da AVL vale para todos os nós.
Objetivo de aprendizagem: calcular o fator com seu sinal e detectar um desequilíbrio que a verificação isolada da raiz não encontra.
Resposta esperada
A tabela segue dos filhos para os pais:
| Nó | Altura | FB |
|---|---|---|
| 5, 50 e 80 | 1 | 0 |
| 10 | 2 | 1 |
| 20 | 3 | 2 |
| 60 | 2 | 1 |
| 70 | 3 | 1 |
| 40 | 4 | 0 |
Não é AVL: o nó 20 tem FB 2, embora a raiz tenha FB 0.
Confira as alturas e os fatores dentro dos nós:
Exercício 2: rastrear uma rotação simples à esquerda
Observe os filhos e as alturas armazenadas antes da chamada.
Faça um trace manual de raiz = rotacionar_esquerda(raiz);, com raiz apontando para 20. Registre as mudanças de ponteiros e alturas e desenhe a árvore resultante.
Apoio em C: altura_no consulta a altura armazenada. atualizar_altura recalcula a altura de um nó a partir dos filhos. rotacionar_esquerda devolve a nova raiz da subárvore.
/* Uma subárvore vazia não acrescenta níveis à altura do pai. */
static int altura_no(const no_avl *no) {
return no == NULL ? 0 : no->altura;
}
/* Recalcula a altura após atualizar as alturas dos filhos de um nó existente. */
static void atualizar_altura(no_avl *no) {
const int altura_esquerda = altura_no(no->esquerda);
const int altura_direita = altura_no(no->direita);
no->altura = 1 + (altura_esquerda > altura_direita ? altura_esquerda : altura_direita);
}
/* Caso RR: raiz e raiz->direita devem existir. A subárvore intermediária muda de pai. */
static no_avl *rotacionar_esquerda(no_avl *raiz) {
no_avl *nova_raiz = raiz->direita;
no_avl *subarvore_transferida = nova_raiz->esquerda;
nova_raiz->esquerda = raiz;
raiz->direita = subarvore_transferida;
atualizar_altura(raiz);
atualizar_altura(nova_raiz);
return nova_raiz;
}
Solução
Explicação
No caso RR, o filho direito assume a raiz local. O nó 30 precisa continuar entre 20 e 40 na ordem da ABB, por isso passa a ser filho direito de 20.
A altura de 20 é atualizada antes da altura de 40. A nova raiz depende da altura já corrigida de seu filho. A atribuição no chamador guarda a raiz devolvida pela função.
Objetivo de aprendizagem: rastrear a rotação RR, preservando a subárvore intermediária e atualizando as alturas na ordem necessária.
Resposta esperada
Os números da tabela identificam os nós apontados:
| Momento | Estado ou ação |
|---|---|
| Entrada | raiz aponta para 20. |
| Referências locais | nova_raiz aponta para 40 e subarvore_transferida para 30. |
| Primeira ligação | nova_raiz->esquerda = raiz faz esquerda de 40 apontar para 20. |
| Transferência | raiz->direita = subarvore_transferida faz direita de 20 apontar para 30. |
| Alturas | Atualiza 20 para altura 2, depois 40 para altura 3. |
| Retorno | A função devolve 40. A variável raiz do chamador passa a apontar para 40. |
Confira o resultado. As ligações alteradas aparecem em verde e com traço mais espesso:
Exercício 3: rastrear uma rotação simples à direita
Observe o estado inicial desta outra árvore.
Faça um trace manual de raiz = rotacionar_direita(raiz);, com raiz apontando para 60. Registre as mudanças de ponteiros e alturas e desenhe a árvore resultante. Use também as auxiliares do exercício 2.
Apoio em C:
/* Caso LL: raiz e raiz->esquerda devem existir. A rotação não aloca nem libera nós. */
static no_avl *rotacionar_direita(no_avl *raiz) {
no_avl *nova_raiz = raiz->esquerda;
no_avl *subarvore_transferida = nova_raiz->direita;
/* Preserva a subárvore intermediária antes de substituir a ligação de seu antigo pai. */
nova_raiz->direita = raiz;
raiz->esquerda = subarvore_transferida;
/* Atualiza primeiro o nó rebaixado, pois a altura do promovido depende dele. */
atualizar_altura(raiz);
atualizar_altura(nova_raiz);
return nova_raiz;
}
Solução
Explicação
No caso LL, o filho esquerdo assume a raiz local. O nó 50 muda de pai, pois sua chave deve continuar maior que 40 e menor que 60. Os nós 30, 20 e 70 mantêm suas ligações.
Objetivo de aprendizagem: rastrear a rotação LL e preservar a subárvore situada entre a raiz antiga e a raiz promovida.
Resposta esperada
| Momento | Estado ou ação |
|---|---|
| Entrada | raiz aponta para 60. |
| Referências locais | nova_raiz aponta para 40 e subarvore_transferida para 50. |
| Primeira ligação | direita de 40 passa a apontar para 60. |
| Transferência | esquerda de 60 passa a apontar para 50. |
| Alturas | Atualiza 60 para altura 2, depois 40 para altura 3. |
| Retorno | A função devolve 40. O chamador atualiza raiz para 40. |
Confira a árvore final:
Exercício 4: rastrear o rebalanceamento de 20
Observe o estado da subárvore que será passada a rebalancear.
Faça um trace manual de raiz = rebalancear(raiz);, com raiz apontando para 20. Registre os fatores consultados, as rotações e o retorno. Desenhe a árvore resultante. Use as rotações dos exercícios 2 e 3.
Apoio em C: fator_balanceamento calcula o FB. rebalancear devolve a raiz corrigida, ou a mesma raiz quando não precisa de rotação. As subárvores dos filhos já devem estar corrigidas antes de tratar o pai.
/* Uma subárvore vazia não acrescenta níveis à altura do pai. */
static int altura_no(const no_avl *no) {
return no == NULL ? 0 : no->altura;
}
/* Recalcula a altura após atualizar as alturas dos filhos de um nó existente. */
static void atualizar_altura(no_avl *no) {
const int altura_esquerda = altura_no(no->esquerda);
const int altura_direita = altura_no(no->direita);
no->altura = 1 + (altura_esquerda > altura_direita ? altura_esquerda : altura_direita);
}
/* Caso RR: raiz e raiz->direita devem existir. A subárvore intermediária muda de pai. */
static no_avl *rotacionar_esquerda(no_avl *raiz) {
no_avl *nova_raiz = raiz->direita;
no_avl *subarvore_transferida = nova_raiz->esquerda;
nova_raiz->esquerda = raiz;
raiz->direita = subarvore_transferida;
atualizar_altura(raiz);
atualizar_altura(nova_raiz);
return nova_raiz;
}
/* Caso LL: raiz e raiz->esquerda devem existir. A rotação não aloca nem libera nós. */
static no_avl *rotacionar_direita(no_avl *raiz) {
no_avl *nova_raiz = raiz->esquerda;
no_avl *subarvore_transferida = nova_raiz->direita;
/* Preserva a subárvore intermediária antes de substituir a ligação de seu antigo pai. */
nova_raiz->direita = raiz;
raiz->esquerda = subarvore_transferida;
/* Atualiza primeiro o nó rebaixado, pois a altura do promovido depende dele. */
atualizar_altura(raiz);
atualizar_altura(nova_raiz);
return nova_raiz;
}
/* O fator usa altura da esquerda menos altura da direita, com o sinal adotado na aula. */
static int fator_balanceamento(const no_avl *no) {
return no == NULL ? 0 : altura_no(no->esquerda) - altura_no(no->direita);
}
/* Restaura uma subárvore após inserir ou remover em uma AVL anteriormente válida. */
static no_avl *rebalancear(no_avl *raiz) {
/* A remoção de uma folha pode deixar uma subárvore sem altura para atualizar. */
if (raiz == NULL) {
return NULL;
}
atualizar_altura(raiz);
const int fator = fator_balanceamento(raiz);
/* O excesso de altura à esquerda exige uma correção LL ou LR. */
if (fator > 1) {
/* O filho inclinado para dentro exige a primeira rotação do caso LR. */
if (fator_balanceamento(raiz->esquerda) < 0) {
raiz->esquerda = rotacionar_esquerda(raiz->esquerda);
}
return rotacionar_direita(raiz);
}
/* O excesso de altura à direita exige uma correção RR ou RL. */
if (fator < -1) {
/* O filho inclinado para dentro exige a primeira rotação do caso RL. */
if (fator_balanceamento(raiz->direita) > 0) {
raiz->direita = rotacionar_direita(raiz->direita);
}
return rotacionar_esquerda(raiz);
}
return raiz;
}
Solução
Explicação
O nó 20 está mais alto à direita, mas seu filho 60 está mais alto à esquerda. Esse é o caso RL. A primeira rotação reorganiza o filho direito, e a segunda corrige a raiz local.
As transferências preservam 50 à esquerda de 60 e 30 à direita de 20. A sequência em Em-Ordem continua a mesma.
Objetivo de aprendizagem: selecionar RL pelos fatores e rastrear a composição das duas rotações sem perder as subárvores intermediárias.
Resposta esperada
| Momento | Estado ou ação |
|---|---|
| Fator da raiz | Atualiza altura de 20 para 4. fator recebe -2, pois . |
| Fator do filho | fator_balanceamento(raiz->direita) calcula 2 - 1 = 1, no nó 60. |
| Primeira rotação | rotacionar_direita(raiz->direita) promove 40, transfere 50 para esquerda de 60 e atualiza as alturas de 60 para 2 e de 40 para 3. |
| Retorno ao pai | direita de 20 recebe o endereço de 40. |
| Segunda rotação | rotacionar_esquerda(raiz), com raiz em 20, promove 40 e transfere 30 para direita de 20. Atualiza 20 para altura 2 e 40 para altura 3. |
| Retorno final | rebalancear devolve 40. O chamador atualiza raiz para 40. |
Confira o resultado da RL, dupla à esquerda:
Exercício 5: rastrear o rebalanceamento de 80
Observe a subárvore com raiz 80.
Faça um trace manual de raiz = rebalancear(raiz);, com raiz apontando para 80. Registre os fatores consultados, as rotações e o retorno. Desenhe a árvore resultante. Use as funções dos exercícios 2 a 4.
Solução
Explicação
O nó 80 está mais alto à esquerda, mas seu filho 40 está mais alto à direita. Esse é o caso LR. A correção começa no filho esquerdo e termina na raiz local.
Objetivo de aprendizagem: selecionar LR pelos fatores e executar suas duas rotações na ordem correta.
Resposta esperada
| Momento | Estado ou ação |
|---|---|
| Fator da raiz | Atualiza altura de 80 para 4. fator recebe 2, pois . |
| Fator do filho | fator_balanceamento(raiz->esquerda) calcula 1 - 2 = -1, no nó 40. |
| Primeira rotação | rotacionar_esquerda(raiz->esquerda) promove 60, transfere 50 para direita de 40 e atualiza as alturas de 40 para 2 e de 60 para 3. |
| Retorno ao pai | esquerda de 80 recebe o endereço de 60. |
| Segunda rotação | rotacionar_direita(raiz), com raiz em 80, promove 60 e transfere 70 para esquerda de 80. Atualiza 80 para altura 2 e 60 para altura 3. |
| Retorno final | rebalancear devolve 60. O chamador atualiza raiz para 60. |
Confira o resultado da LR, dupla à direita:
Exercício 6: acompanhar duas inserções consecutivas
Observe a AVL inicial. A caixa da raiz já existe, e *arvore aponta para o nó 50.
Faça o trace manual de inserir_avl(arvore, 5) e, em seguida, inserir_avl(arvore, 2). Para cada chamada, registre o retorno e as atualizações relevantes e desenhe a árvore resultante. Use rebalancear e suas auxiliares dos exercícios 2 a 4.
Apoio em C: o tipo resultado_insercao_avl usa nomes para distinguir os resultados. AVL_INSERIDO significa inserção realizada. Os outros valores indicam duplicata, falta de memória e referência inválida. criar_no_avl cria uma folha, e inserir_no devolve a raiz da subárvore após a tentativa de inserção.
/* Resultados explícitos distinguem duplicata, falta de memória e referência inválida. */
typedef enum {
AVL_INSERIDO,
AVL_CHAVE_DUPLICADA,
AVL_SEM_MEMORIA,
AVL_ARVORE_INVALIDA
} resultado_insercao_avl;
/* Aloca uma folha. sizeof(*no) mede o nó, e não o ponteiro. */
static no_avl *criar_no_avl(int chave) {
no_avl *no = malloc(sizeof(*no));
/* Informa a falha antes de escrever em um endereço inválido. */
if (no == NULL) {
return NULL;
}
no->chave = chave;
no->altura = 1;
no->esquerda = NULL;
no->direita = NULL;
return no;
}
/* Desce como em uma ABB e corrige os ancestrais no retorno da recursão. */
static no_avl *inserir_no(no_avl *raiz, int chave, resultado_insercao_avl *resultado) {
/* Somente uma posição vazia pode receber a nova folha. */
if (raiz == NULL) {
no_avl *no = criar_no_avl(chave);
*resultado = no == NULL ? AVL_SEM_MEMORIA : AVL_INSERIDO;
return no;
}
/* Uma chave já presente não cria outro nó nem altera a árvore. */
if (chave == raiz->chave) {
*resultado = AVL_CHAVE_DUPLICADA;
return raiz;
}
/* A ordem da ABB determina o único ramo que pode receber a chave. */
if (chave < raiz->chave) {
raiz->esquerda = inserir_no(raiz->esquerda, chave, resultado);
} else {
raiz->direita = inserir_no(raiz->direita, chave, resultado);
}
/* Uma inserção rejeitada ou sem memória não alterou a subárvore. */
if (*resultado != AVL_INSERIDO) {
return raiz;
}
return rebalancear(raiz);
}
/* Atualiza o conteúdo da caixa, preservando seu endereço mesmo quando a raiz muda. */
static resultado_insercao_avl inserir_avl(arvore_avl *arvore, int chave) {
/* Sem caixa, não existe uma posição válida para guardar a raiz. */
if (arvore == NULL) {
return AVL_ARVORE_INVALIDA;
}
resultado_insercao_avl resultado = AVL_SEM_MEMORIA;
*arvore = inserir_no(*arvore, chave, &resultado);
return resultado;
}
Solução
Explicação
Inserir 5 aumenta as alturas do caminho, mas mantém todos os fatores no intervalo permitido. Inserir 2 exige uma correção na subárvore que começa em 10.
O retorno dessa subárvore atualiza esquerda de 25. A raiz geral continua sendo 50. A recursão permite corrigir uma raiz interna sem perder a ligação com seu pai.
Objetivo de aprendizagem: acompanhar a manutenção da AVL durante a inserção, distinguindo atualização de altura de rotação e reconectando uma raiz interna.
Resposta esperada
Primeira chamada: inserir 5. A tabela acompanha a descida e o retorno:
| Momento | Estado ou ação |
|---|---|
| Descida | Visita 50, 25 e 10, seguindo esquerda. |
| Posição vazia | Cria a folha 5, com altura 1. resultado recebe AVL_INSERIDO. |
| Retorno em 10 | esquerda recebe 5. Altura 2, FB 1, sem rotação. |
| Retorno em 25 | Altura 3, FB 1, sem rotação. |
| Retorno em 50 | Altura 4, FB 1, sem rotação. |
| Saída | *arvore continua apontando para 50. Retorno AVL_INSERIDO. |
Segunda chamada: inserir 2 na árvore que já contém 5.
| Momento | Estado ou ação |
|---|---|
| Descida | Visita 50, 25, 10 e 5, seguindo esquerda. |
| Posição vazia | Cria a folha 2, com altura 1. resultado recebe AVL_INSERIDO. |
| Retorno em 5 | esquerda recebe 2. Altura 2, FB 1, sem rotação. |
| Retorno em 10 | Altura provisória 3, FB 2. O filho 5 tem FB 1, então aplica LL com rotacionar_direita(raiz), no nó 10. |
| Rotação e ligação ao pai | 10 fica sem filhos, com altura 1. A raiz local passa a ser 5, com filhos 2 e 10 e altura 2. esquerda de 25 recebe 5. |
| Retorno em 25 e 50 | 25 mantém altura 3, FB 1. 50 mantém altura 4, FB 1. Não há outra rotação. |
| Saída | *arvore continua apontando para 50. Retorno AVL_INSERIDO. |
Exercício 7: acompanhar uma remoção
Observe a AVL antes da chamada. A caixa da raiz já existe, e *arvore aponta para 40.
Faça um trace manual de remover_avl(arvore, 60). Registre as decisões, as alterações e o retorno. Desenhe a árvore resultante. Use rebalancear e suas auxiliares dos exercícios 2 a 4.
Apoio em C: remover_no devolve a raiz da subárvore após a remoção. Para um nó com dois filhos, este programa copia a chave do sucessor, o menor nó da subárvore direita, e remove sua ocorrência antiga. Essa é a política do complemento C da aula de AVL. A implementação de ABB da prática 9 usa outra política.
/* O nó mais à esquerda contém a menor chave de uma subárvore não vazia. */
static no_avl *no_minimo(no_avl *raiz) {
no_avl *no = raiz;
/* Descarta a cada passo os nós que não podem conter a menor chave. */
while (no->esquerda != NULL) {
no = no->esquerda;
}
return no;
}
/* Remove como em uma ABB e rebalanceia os ancestrais afetados no retorno. */
static no_avl *remover_no(no_avl *raiz, int chave, bool *removido) {
/* Uma posição vazia comprova que a chave procurada não existe. */
if (raiz == NULL) {
return NULL;
}
/* A igualdade identifica o nó cuja chave deve sair da árvore. */
if (chave == raiz->chave) {
*removido = true;
/* Preserva o filho direito antes de liberar um nó sem filho esquerdo. */
if (raiz->esquerda == NULL) {
no_avl *direita = raiz->direita;
free(raiz);
return direita;
}
/* O filho esquerdo substitui o nó cuja subárvore direita está vazia. */
if (raiz->direita == NULL) {
no_avl *esquerda = raiz->esquerda;
free(raiz);
return esquerda;
}
no_avl *sucessor = no_minimo(raiz->direita);
raiz->chave = sucessor->chave;
bool sucessor_removido = false;
raiz->direita = remover_no(raiz->direita, sucessor->chave, &sucessor_removido);
} else {
/* Sem igualdade, a ordem da ABB seleciona o único ramo possível. */
if (chave < raiz->chave) {
raiz->esquerda = remover_no(raiz->esquerda, chave, removido);
} else {
raiz->direita = remover_no(raiz->direita, chave, removido);
}
}
/* Uma chave ausente deve preservar a subárvore e seus endereços. */
if (!*removido) {
return raiz;
}
return rebalancear(raiz);
}
/* Informa se uma chave foi removida. Referência inválida ou chave ausente resulta em false. */
static bool remover_avl(arvore_avl *arvore, int chave) {
/* Rejeita a referência inválida antes de acessar a árvore. */
if (arvore == NULL) {
return false;
}
bool removido = false;
*arvore = remover_no(*arvore, chave, &removido);
return removido;
}
Solução
Explicação
A remoção da folha 60 deixa 40 com FB 2. Seu filho esquerdo, 20, tem FB 0. O código aplica uma rotação simples à direita, pois a condição para a primeira parte de LR exige FB negativo no filho.
O caso de filho com FB zero também pertence à rotação simples. A árvore perde um nó, mas sua altura permanece 3.
Objetivo de aprendizagem: rastrear a remoção de uma folha e escolher a rotação simples quando o filho mais alto tem FB zero.
Resposta esperada
| Momento | Estado ou ação |
|---|---|
| Entrada | removido começa como false. A busca visita 40 e segue direita até 60. |
| Chave encontrada | Em 60, *removido recebe true. Como esquerda é NULL, guarda direita, que também é NULL. |
| Liberação | Libera o nó 60 e devolve NULL. O campo direita de 40 recebe esse retorno. |
| Verificação de 40 | Altura 3, FB 2. O filho 20 tem FB 0. A condição fator_balanceamento(raiz->esquerda) < 0 é falsa. |
| Correção | rotacionar_direita(raiz), no nó 40, promove 20 e transfere 30 para esquerda de 40. Atualiza 40 para altura 2 e 20 para altura 3. |
| Saída | *arvore passa a apontar para 20. Retorno true. |
Confira a árvore final:
Exercício 8: continuar a correção nos ancestrais
Observe a AVL inicial. As alturas aparecem nos nós para apoiar o trace.
Faça um trace manual de remover_avl(arvore, 20), com a caixa já criada e *arvore apontando para 50. Registre as decisões, as alterações e o retorno. Desenhe a árvore resultante. Use as funções do exercício 7 e as auxiliares dos exercícios 2 a 4.
Solução
Explicação
Como 20 tem dois filhos, o programa copia a chave 30 para seu registro e libera a antiga ocorrência do sucessor. Depois, a subárvore perde altura e precisa de uma rotação.
A correção local reduz sua altura de 3 para 2. Isso também altera o equilíbrio do ancestral 50. A remoção continua verificando os ancestrais no retorno da recursão.
Objetivo de aprendizagem: rastrear a remoção com sucessor e reconhecer que uma única remoção pode exigir correções em mais de um ancestral.
Resposta esperada
| Momento | Estado ou ação |
|---|---|
| Entrada e descida | removido = false. Em 50, segue esquerda até 20. |
| Chave encontrada | O nó tem dois filhos. *removido = true. no_minimo(raiz->direita) encontra o sucessor 30. |
| Substituição | Copia 30 para chave do registro que guardava 20. Inicia sucessor_removido = false e chama a remoção de 30 na subárvore direita. |
| Remoção do sucessor | A antiga folha 30 é liberada, sucessor_removido recebe true e a chamada retorna NULL. O registro que agora guarda 30 fica sem filho direito. |
| Primeira verificação | Na raiz local 30: altura provisória 3, FB 2. Seu filho 10 tem FB 1. |
| Primeira correção | LL: rotacionar_direita(raiz), no nó 30, promove 10. O nó 30 fica sem filhos, com altura 1. O nó 10 fica com filhos 5 e 30 e altura 2. |
| Retorno ao ancestral | O campo esquerda de 50 recebe 10. Em 50, as alturas dos filhos agora são 2 e 4, então seu FB é -2. |
| Segunda verificação | O filho direito 80 tem FB -1. Aplica RR com rotacionar_esquerda(raiz), no nó 50. |
| Segunda correção | 80 assume a raiz. O nó 65 passa a ser filho direito de 50. Atualiza 50 para altura 3 e 80 para altura 4. |
| Saída | *arvore passa a apontar para 80. Retorno true. |
Confira a árvore final, com 11 nós. Apenas o registro da antiga folha sucessora foi liberado: