Lista de exercícios

Aula prática 09: árvores binárias de busca

16 exercícios sobre representação, operações, percursos, medidas e balanceamento de ABB, com tarefas curtas e soluções comentadas.

Esta lista é para praticar o conteúdo, sem correção individual. Use Resposta esperada para comparação e autocorreção. Faça os desenhos no papel ou na ferramenta que preferir. Os trechos em C usam a representação e os nomes da implementação da aula. Não é necessário montar um programa completo. O tempo estimado inclui tentativa e conferência.

Convenções: cada exercício começa no estado apresentado, independentemente dos anteriores. Dentro de uma sequência, as operações são cumulativas. NULL significa ausência de nó ou de endereço válido, conforme o contexto. A raiz está no nível 1. A altura conta níveis: árvore vazia tem altura 0 e folha tem altura 1. Considere as alocações bem-sucedidas, salvo quando o exercício apresentar uma falha.

Representação em C: cada struct NO guarda info, esq e dir. O tipo ArvBin representa um ponteiro para nó. ArvBin* raiz aponta para uma alocação separada que guarda esse ponteiro, chamada aqui de caixa da raiz.

/* Um nó guarda a chave e os endereços dos filhos. */
struct NO{
    int info;
    struct NO *esq;
    struct NO *dir;
};

/* ArvBin representa o endereço de um nó. */
typedef struct NO* ArvBin;

raiz é o endereço da caixa. *raiz é seu conteúdo, o endereço do primeiro nó. (*raiz)->info acessa a chave desse nó. -> acessa um campo pelo ponteiro e & obtém um endereço. Assim, &((*raiz)->esq) permite tratar o campo esquerdo como a caixa de uma subárvore. Só use *raiz se raiz != NULL e só acesse os campos se *raiz != NULL.

= atribui, == compara e return encerra a chamada atual. malloc reserva memória e pode retornar NULL. free libera uma alocação sem atualizar automaticamente quem guardava seu endereço. Os trechos de funções pressupõem as declarações da implementação e as bibliotecas <stdlib.h> e <stdio.h>. As funções auxiliares libera_NO e remove_atual são usadas dentro de ArvoreBinaria.c.

Exercício 1: verificar a propriedade da ABB

Observe a posição de cada nó. Para verificar uma ABB, considere todos os descendentes de cada subárvore.

Árvore com raiz 40, filho esquerdo 20, filho direito 60 e filho direito 50 de 20; cada campo ausente aparece como NULL

Classifique a estrutura e justifique:

  1. É uma árvore binária?
  2. É uma árvore binária de busca?
Solução

Explicação

Uma árvore binária admite no máximo dois filhos por nó e distingue esquerda de direita. Isso não determina a ordem dos valores. Embora 50 seja maior que seu pai 20, ele está na subárvore esquerda de 40. A propriedade da ABB também precisa valer em relação a esse ancestral.

Objetivo de aprendizagem: verificar a ordenação de uma subárvore inteira, distinguindo uma árvore binária de uma ABB.

Resposta esperada

É uma árvore binária: nenhum nó possui mais de dois filhos, e suas posições estão identificadas. Não é uma ABB: 50 pertence à subárvore esquerda de 40, mas 50>4050>40. Comparar somente cada filho com seu pai não detectaria essa violação.

Exercício 2: criar a árvore sem criar um nó

Observe os estados A, B e C. A e B mostram o resultado da criação. Em C, um nó já foi inserido.

Três estados de memória: raiz nula, raiz apontando para uma caixa nula e raiz apontando para uma caixa ligada ao nó 26

Responda com base na imagem:

  1. Associe A, B e C a: falha de criação, árvore vazia e árvore com um nó.
  2. Em C, quais são os valores de info, esq e dir?
  3. Por que a caixa da raiz não conta como nó?
Solução

Explicação

cria_ArvBin reserva a caixa da raiz. Ela ainda não cria um struct NO. malloc faz essa reserva, e sizeof(ArvBin) informa o espaço necessário.

ArvBin* cria_ArvBin(){
    ArvBin* raiz = (ArvBin*) malloc(sizeof(ArvBin));
    if(raiz != NULL)
        *raiz = NULL;
    return raiz;
}

Se a reserva falha, raiz recebe NULL. Se funciona, a caixa existe e guarda NULL até a inserção do primeiro nó.

Objetivo de aprendizagem: distinguir a referência de controle, a árvore vazia e o primeiro nó para interpretar corretamente a criação da ABB.

Resposta esperada

  1. A: falha de criação. B: árvore criada e vazia. C: árvore com um nó.
  2. Em C: (*raiz)->info = 26, (*raiz)->esq = NULL e (*raiz)->dir = NULL.
  3. A caixa guarda o endereço do primeiro nó. Ela não tem os campos de um struct NO e não ocupa uma posição na árvore.

Exercício 3: inserir chaves e descartar uma duplicata

Faça um rastreamento manual das inserções. Use o código abaixo como referência para acompanhar os passos no desenho.

novo é o nó candidato. atual percorre a árvore, e ant guarda o pai.

As verificações iniciais tratam falhas de alocação. O laço procura uma posição vazia e descarta duplicatas. A comparação final liga o novo nó ao pai.

A função retorna 1 ao inserir e 0 para duplicata, falha de alocação ou caixa inexistente.

int insere_ArvBin(ArvBin* raiz, int valor){
    if(raiz == NULL)
        return 0;
    struct NO* novo;
    novo = (struct NO*) malloc(sizeof(struct NO));
    if(novo == NULL)
        return 0;
    novo->info = valor;
    novo->dir = NULL;
    novo->esq = NULL;

    if(*raiz == NULL)
        *raiz = novo;
    else{
        struct NO* atual = *raiz;
        struct NO* ant = NULL;
        while(atual != NULL){
            ant = atual;
            if(valor == atual->info){
                free(novo);
                return 0;//elemento já existe
            }

            if(valor > atual->info)
                atual = atual->dir;
            else
                atual = atual->esq;
        }
        if(valor > ant->info)
            ant->dir = novo;
        else
            ant->esq = novo;
    }
    return 1;
}

Considere que ArvBin* raiz = cria_ArvBin(); já criou uma árvore vazia. Faça um trace manual do código simulando as inserções de 32, 18, 46, 25, 38 e 18, nessa ordem, com insere_ArvBin(raiz, valor).

Desenhe os estados intermediários que precisar para acompanhar as inserções. Na última tentativa, anote o retorno da função.

Na resposta esperada, você encontrará apenas a árvore final e o retorno da última tentativa para conferir seu raciocínio.

Solução

Explicação

Cada chave segue as comparações desde a raiz até uma posição vazia. Para 25, o caminho passa por 32 e 18. Para 38, passa por 32 e 46. Na última tentativa, o laço encontra valor == atual->info no nó 18. O nó candidato é liberado e a árvore permanece igual.

Objetivo de aprendizagem: executar inserções preservando a ordem da ABB e reconhecer que uma duplicata não cria outro nó.

Resposta esperada

Resultado das inserções: raiz 32, subárvore esquerda com 18 e 25, subárvore direita com 46 e 38; cinco nós e nenhum novo nó para a duplicata

A última tentativa de inserir 18 retorna 0, pois a chave já existe. A árvore permanece com 5 nós.

Exercício 4: rastrear uma busca com e sem sucesso

Observe os filhos de cada nó. Os campos NULL mostram onde uma busca pode terminar sem encontrar a chave.

ABB de raiz 45, com 20 e 70 como filhos, 30 à direita de 20 e 55 à esquerda de 70; os campos sem filhos são NULL

Rastreie as buscas por 55 e por 25, cada uma começando na raiz. Use busca_ArvBin(raiz, valor).

Para cada busca, registre:

  1. Os nós examinados, na ordem.
  2. A direção de cada descida (esq ou dir).
  3. O retorno da função. Inclua o NULL final no caminho se a chave estiver ausente.

Apoio em C:

Leia o código de busca da aula. atual começa no primeiro nó. O laço compara a chave e segue apenas uma subárvore.

A busca retorna o endereço do nó encontrado ou NULL. O return 0 inicial também representa um ponteiro nulo.

struct NO* busca_ArvBin(ArvBin *raiz, int valor){
    if(raiz == NULL)
        return 0;
    struct NO* atual = *raiz;
    while(atual != NULL){
        if(valor == atual->info){
            return atual;
        }
        if(valor > atual->info)
            atual = atual->dir;
        else
            atual = atual->esq;
    }
    return NULL;
}
Solução

Explicação

55 é maior que 45 e menor que 70. A igualdade com o próximo nó encerra essa busca. Já 25 é menor que 45, maior que 20 e menor que 30. O último passo procura o filho esquerdo de 30, que não existe. Não é necessário examinar o outro lado da árvore.

Objetivo de aprendizagem: selecionar a subárvore possível em cada comparação e distinguir encontro de chave de término em posição vazia.

Resposta esperada

  • Busca por 55: 45 → direita → 70 → esquerda → 55. Retorna a referência ao nó de chave 55.
  • Busca por 25: 45 → esquerda → 20 → direita → 30 → esquerda → NULL. Retorna NULL.

Exercício 5: rastrear a exibição em Pré-Ordem

Observe as ligações da árvore. O percurso pode começar em qualquer nó e visitar a subárvore que começa nele.

Árvore com raiz 18 e ponteiro no apontando para 7. O nó 7 tem filhos 3 e 12; 3 tem filhos 1 e 5; 5 tem filhos 4 e 6; 12 tem filhos 9 e 15. O nó 29 é filho direito de 18

Qual sequência será exibida por preOrdem_NO(no), com no apontando para o nó 7? Escreva os valores na ordem de exibição.

Apoio em C:

Use a mesma struct NO da aula. A função recebe diretamente um ponteiro para nó. Ela exibe info, percorre esq e depois dir.

/* Exibe em Pré-Ordem a subárvore que começa em no. */
void preOrdem_NO(struct NO* no){
    /* Encerra o ramo vazio para evitar acesso a um nó inexistente. */
    if(no == NULL)
        return;
    printf("%d\n", no->info);
    preOrdem_NO(no->esq);
    preOrdem_NO(no->dir);
}
Solução

Explicação

O percurso exibe 7 e conclui sua subárvore esquerda, iniciada em 3, antes de começar a direita, iniciada em 12.

Em 3, visita primeiro 1 e depois a subárvore de 5. O nó 5 é exibido antes de seus filhos 4 e 6. Na subárvore direita de 7, visita 12, depois 9 e 15. Os nós 18 e 29 ficam fora do percurso.

Objetivo de aprendizagem: rastrear a visita antes das subárvores para determinar a sequência em Pré-Ordem.

Resposta esperada

7, 3, 1, 5, 4, 6, 12, 9, 15.

Exercício 6: rastrear a exibição em Em-Ordem

Observe a mesma árvore do exercício 5. O ponteiro no indica o início do percurso.

Árvore com raiz 18 e ponteiro no apontando para 7. O nó 7 tem filhos 3 e 12; 3 tem filhos 1 e 5; 5 tem filhos 4 e 6; 12 tem filhos 9 e 15. O nó 29 é filho direito de 18

Faça um trace manual de exibir_em_ordem(no), com no apontando para o nó 7, e escreva a sequência exibida.

Apoio em C:

void exibir_em_ordem(struct NO* no) {
    if (no == NULL) {
        return;
    }
    exibir_em_ordem(no->esq);
    printf("%d\n", no->info);
    exibir_em_ordem(no->dir);
}
Solução

Explicação

Em-Ordem conclui a subárvore esquerda, exibe o nó e percorre a direita. Como o percurso começa em 7, os nós 18 e 29 ficam de fora. Os valores da subárvore de 7 aparecem em ordem crescente porque ela é uma ABB.

Objetivo de aprendizagem: rastrear a visita entre as subárvores para determinar a sequência em Em-Ordem.

Resposta esperada

O trace pode ser agrupado pelas partes da subárvore de 7:

Parte visitadaValores exibidos
Subárvore esquerda de 71, 3, 4, 5, 6
Nó 77
Subárvore direita de 79, 12, 15

Sequência completa: 1, 3, 4, 5, 6, 7, 9, 12, 15.

Exercício 7: rastrear a exibição em Pós-Ordem

Observe a mesma árvore do exercício 5. O ponteiro no indica o início do percurso.

Árvore com raiz 18 e ponteiro no apontando para 7. O nó 7 tem filhos 3 e 12; 3 tem filhos 1 e 5; 5 tem filhos 4 e 6; 12 tem filhos 9 e 15. O nó 29 é filho direito de 18

Faça um trace manual de exibir_pos_ordem(no), com no apontando para o nó 7, e escreva a sequência exibida.

Apoio em C:

void exibir_pos_ordem(struct NO* no) {
    if (no == NULL) {
        return;
    }
    exibir_pos_ordem(no->esq);
    exibir_pos_ordem(no->dir);
    printf("%d\n", no->info);
}
Solução

Explicação

Pós-Ordem conclui as subárvores esquerda e direita antes de exibir o nó. Assim, 3 aparece depois de seus descendentes, 12 aparece depois de 9 e 15, e 7 é o último valor exibido. Os nós 18 e 29 ficam fora do percurso.

Objetivo de aprendizagem: rastrear a visita depois das subárvores para determinar a sequência em Pós-Ordem.

Resposta esperada

O trace pode ser agrupado pelas partes da subárvore de 7:

Parte visitadaValores exibidos
Subárvore esquerda de 71, 4, 6, 5, 3
Subárvore direita de 79, 15, 12
Nó 77

Sequência completa: 1, 4, 6, 5, 3, 9, 15, 12, 7.

Exercício 8: liberar a memória de todos os nós da árvore

Considere o fim do uso da árvore: todos os nós serão liberados da memória.

Árvore com raiz 18, filhos 7 e 29 e descendente 11 à direita de 7; as setas mostram os campos que devem continuar acessíveis durante a liberação

Onde colocar free(no); para destruir todos os nós?

  1. Indique A, B ou C para liberar todos os nós com segurança. Deixe as outras posições vazias.
  2. Escreva a ordem dos valores de info dos registros liberados.

Apoio em C:

A função libera_NO é a auxiliar recursiva de libera_ArvBin no código da aula. Os comentários A, B e C marcam as posições possíveis para acrescentar free(no);.

free(no) libera apenas o registro indicado. Seus campos não podem ser acessados depois da liberação. A função percorre esq antes de dir.

/* Libera os nós; a posição da liberação deve preservar os filhos. */
void libera_NO(struct NO* no){
    /* Uma posição vazia não possui registro para liberar. */
    if(no == NULL)
        return;
    /* A */
    libera_NO(no->esq);
    /* B */
    libera_NO(no->dir);
    /* C */
}
Solução

Explicação

O registro do pai precisa existir enquanto suas referências aos filhos são usadas. A liberação deve acontecer depois das duas chamadas, em Pós-Ordem. Assim, a chamada de 7 termina antes da chamada de 29, e a raiz é liberada por último.

Essa é uma destruição completa. As ligações não precisam ser reorganizadas, pois nenhum nó será mantido para uso posterior.

Objetivo de aprendizagem: escolher o momento da liberação para preservar o acesso aos filhos durante a destruição recursiva.

Resposta esperada

Posição C, imediatamente depois de libera_NO(no->dir);. A ordem de liberação é 11, 7, 29, 18.

Exercício 9: encerrar a destruição da caixa da raiz

Considere que raiz aponta para uma caixa válida. libera_NO(*raiz) já libera todos os nós, mas a caixa é uma alocação separada.

/* Libera os nós e, em seguida, a caixa da raiz. */
void libera_ArvBin(ArvBin* raiz){
    /* Sem caixa, não há memória a devolver. */
    if(raiz == NULL)
        return;
    libera_NO(*raiz);
    /* Acrescente aqui a liberação da caixa. */
}

No chamador, a variável ArvBin* raiz já existe:

libera_ArvBin(raiz);
/* Acrescente aqui a atualização da variável do chamador. */

Complete os dois pontos indicados com instruções em C:

  1. Na função, libere a caixa da raiz.
  2. No chamador, faça raiz deixar de guardar o endereço liberado.
Solução

Explicação

raiz e *raiz se referem a alocações diferentes:

  • raiz: endereço da caixa alocada por cria_ArvBin.
  • *raiz: conteúdo da caixa, que guarda o endereço do primeiro nó ou NULL quando a árvore está vazia.

libera_NO(*raiz) já libera todos os nós, inclusive o primeiro. Depois disso, usamos free(raiz) para liberar a caixa. Usar free(*raiz) não liberaria a caixa e, se a árvore tinha nós, tentaria liberar o primeiro nó novamente.

A função recebe uma cópia do endereço da caixa. Por isso, o próprio chamador precisa atualizar sua variável após o retorno. Ele não deve liberar a caixa novamente.

Objetivo de aprendizagem: concluir a liberação da representação, distinguindo a memória dos nós da memória da caixa da raiz.

Resposta esperada

Na função, acrescente free(raiz);. O resultado coincide com o código da aula:

void libera_ArvBin(ArvBin* raiz){
    if(raiz == NULL)
        return;
    libera_NO(*raiz);//libera cada nó
    free(raiz);//libera a raiz
}

No chamador:

libera_ArvBin(raiz);
raiz = NULL;

Exercício 10: distinguir contagem e altura

Compare duas funções que recebem um ponteiro para o nó inicial de uma subárvore:

/* Retorna a quantidade de nós da subárvore que começa em no. */
int contar_nos(struct NO* no);

/* Retorna a altura em níveis da subárvore que começa em no. */
int calcular_altura(struct NO* no);

no pode apontar para qualquer nó. Quando no == NULL, as duas funções já retornam 0.

Dentro de contar_nos, as contagens das subárvores já foram calculadas:

int total_esquerda = contar_nos(no->esq);
int total_direita = contar_nos(no->dir);

Dentro de calcular_altura, as alturas das subárvores também já foram calculadas:

int altura_esquerda = calcular_altura(no->esq);
int altura_direita = calcular_altura(no->dir);

Complete o final de cada função, em C:

  1. Escreva o return de contar_nos.
  2. Escreva o final de calcular_altura. Use if para escolher o caminho mais longo.

Não é necessário escrever as funções inteiras.

Solução

Explicação

A contagem reúne todos os nós das duas subárvores e inclui o nó atual. A altura acompanha somente o caminho mais longo e acrescenta o nível do nó atual. As funções combinam os retornos recursivos de maneiras diferentes.

Um ponteiro para nó basta para acessar seus filhos e consultar a subárvore. Essas funções não alteram qual nó o ponteiro do chamador indica, por isso recebem struct NO* no.

Objetivo de aprendizagem: distinguir a soma de quantidades da escolha do maior caminho ao combinar resultados recursivos de subárvores.

Resposta esperada

Final de contar_nos:

return 1 + total_esquerda + total_direita;

Final de calcular_altura, como na implementação:

/* Apenas o maior caminho determina a altura. */
if (altura_esquerda > altura_direita) {
    return 1 + altura_esquerda;
}
return 1 + altura_direita;

Em ambas, uma folha retorna 1, pois suas duas subárvores vazias retornam 0.

Exercício 11: rastrear a remoção de uma folha

Considere que raiz aponta para a caixa da árvore abaixo.

Raiz 52 ligada por esq a 24, cujo campo dir aponta para a folha 36; a folha tem esq e dir iguais a NULL

Faça um trace manual da chamada remove_ArvBin(raiz, 36) usando as duas funções abaixo.

Apoio em C:

int remove_ArvBin(ArvBin *raiz, int valor){
    if(raiz == NULL)
        return 0;
    struct NO* ant = NULL;
    struct NO* atual = *raiz;
    while(atual != NULL){
        if(valor == atual->info){
            if(atual == *raiz)
                *raiz = remove_atual(atual);
            else{
                if(ant->dir == atual)
                    ant->dir = remove_atual(atual);
                else
                    ant->esq = remove_atual(atual);
            }
            return 1;
        }
        ant = atual;
        if(valor > atual->info)
            atual = atual->dir;
        else
            atual = atual->esq;
    }
    return 0;
}
struct NO* remove_atual(struct NO* atual){
    struct NO *no1, *no2;
    if(atual->esq == NULL){
        no2 = atual->dir;
        free(atual);
        return no2;
    }
    no1 = atual;
    no2 = atual->esq;
    while(no2->dir != NULL){
        no1 = no2;
        no2 = no2->dir;
    }

    if(no1 != atual){
        no1->dir = no2->esq;
        no2->esq = atual->esq;
    }
    no2->dir = atual->dir;
    free(atual);
    return no2;
}
Solução

Explicação

remove_ArvBin localiza 36 e usa o retorno de remove_atual para atualizar a ligação no pai. Como 36 é uma folha, a auxiliar retorna NULL.

Objetivo de aprendizagem: remover uma folha substituindo sua referência por NULL, sem alterar os demais nós.

Resposta esperada

Um trace possível é o da tabela. Os números identificam o campo info dos nós apontados.

MomentoEstado ou ação
Inícioant é NULL e atual aponta para 52.
Primeira descidaant aponta para 52 e atual para 24.
Segunda descidaant aponta para 24 e atual para 36.
Chave encontradaChama remove_atual(atual).
Primeiro ifComo atual não tem filho esquerdo, entra no primeiro if.
Liberação do nóNa auxiliar, no2 recebe NULL, o nó 36 é liberado e a chamada retorna NULL.
Retorno ao chamadorant->dir recebe NULL e remove_ArvBin retorna 1.

Confira a árvore após a remoção:

Após remover 36, a raiz continua sendo 52 e seu filho esquerdo continua sendo 24. O campo dir de 24 agora é NULL e está destacado. Somente o nó 36 teve sua memória liberada

Exercício 12: rastrear a remoção da raiz com um filho

Considere que raiz aponta para a caixa da árvore abaixo.

Raiz 52 com esq NULL e dir apontando para 76, cujo campo esq aponta para 60; todos os campos ausentes são NULL

Faça um trace manual da chamada remove_ArvBin(raiz, 52) usando as funções remove_ArvBin e remove_atual do exercício 11.

Solução

Explicação

A auxiliar retorna o endereço de 76, que passa a ser guardado em *raiz. Somente o nó 52 é liberado. O nó 76 mantém sua ligação com 60, e a caixa da raiz permanece alocada.

Objetivo de aprendizagem: promover uma subárvore inteira ao remover uma raiz com um filho, preservando seus descendentes.

Resposta esperada

Um trace possível é o da tabela. Os números identificam o campo info dos nós apontados.

MomentoEstado ou ação
Inícioant é NULL e atual aponta para 52.
Chave encontradavalor == atual->info e atual == *raiz. Chama remove_atual(atual) para atualizar a caixa da raiz.
Primeiro if da auxiliarComo atual não tem filho esquerdo, entra no primeiro if.
Liberação do nóno2 recebe o endereço de 76, o nó 52 é liberado e a auxiliar retorna no2.
Retorno ao chamador*raiz recebe o endereço de 76 e remove_ArvBin retorna 1.

Confira a árvore após a remoção. A seta mais espessa destaca a atualização de *raiz:

Após remover 52, a caixa da raiz aponta para 76, com essa referência destacada. O campo esq de 76 continua apontando para 60. Somente o nó 52 teve sua memória liberada

Exercício 13: localizar os substitutos de um nó com dois filhos

Observe as duas subárvores da raiz 60. Para achar o maior valor de uma subárvore, siga dir. Para achar o menor, siga esq.

Árvore de raiz 60, com subárvore esquerda enraizada em 30, contendo 45 e 40, e direita enraizada em 90, contendo 75; campos esq e dir indicam os caminhos possíveis

Localize os dois candidatos a substituir 60:

  1. O maior nó da subárvore esquerda.
  2. O menor nó da subárvore direita.

Para cada candidato, escreva o caminho a partir da raiz da subárvore e o valor de info. A função remove_atual da aula usa o maior da esquerda.

Solução

Explicação

A procura do maior começa na raiz da subárvore esquerda e termina quando não há filho direito. A procura do menor começa na raiz da subárvore direita e termina quando não há filho esquerdo. São os vizinhos de 60 na ordem crescente.

Objetivo de aprendizagem: localizar os extremos que preservam a ordenação ao substituir um nó com dois filhos.

Resposta esperada

  • Maior da esquerda: caminho 30 → 45, candidato 45.
  • Menor da direita: caminho 90 → 75, candidato 75.

Exercício 14: rastrear a remoção da raiz com dois filhos

Considere que raiz aponta para a caixa da árvore abaixo.

Árvore antes da remoção de 64: à esquerda 32 com filhos 16 e 48, sendo 40 filho esquerdo de 48; à direita 88 com filho esquerdo 80

Faça um trace manual da chamada remove_ArvBin(raiz, 64) usando as funções remove_ArvBin e remove_atual do exercício 11.

Perceba que nesse caso, vai acontecer uma busca pelo nó de maior valor da subárvore esquerda.

Solução

Explicação

48 substitui 64 na raiz. Antes disso, o campo dir de 32 recebe o endereço de 40, preservando esse descendente. O nó 48 recebe as subárvores de raízes 32 e 88. Somente o nó 64 é liberado.

Objetivo de aprendizagem: atualizar as referências da remoção com dois filhos sem perder a subárvore do nó promovido.

Resposta esperada

Um trace possível é o da tabela. Os números identificam o campo info dos nós apontados.

MomentoEstado ou ação
Inícioant é NULL e atual aponta para 64.
Chave encontradavalor == atual->info e atual == *raiz. Chama remove_atual(atual) para atualizar a caixa da raiz.
Primeiro if da auxiliarComo atual tem filho esquerdo, não entra no primeiro if. Em seguida, no1 aponta para 64 e no2 para 32.
Laço da auxiliarno2->dir aponta para 48. Após uma repetição, no1 aponta para 32 e no2 para 48. O laço termina, pois no2->dir == NULL.
Segundo if da auxiliarno1 != atual, então entra no if. no1->dir = no2->esq; faz o campo dir de 32 receber o endereço de 40.
Ligação à esquerdano2->esq = atual->esq; faz o campo esq de 48 receber o endereço de 32.
Ligação à direitano2->dir = atual->dir; faz o campo dir de 48 receber o endereço de 88.
Liberação do nófree(atual); libera o nó 64. A auxiliar retorna no2, que aponta para 48.
Retorno ao chamador*raiz recebe o endereço de 48 e remove_ArvBin retorna 1.

Confira a árvore após a remoção. As setas mais espessas destacam as referências alteradas:

Depois da remoção: a caixa aponta para 48, com filhos 32 e 88; 32 mantém 16 e recebe 40 à direita; 88 mantém 80 à esquerda

Exercício 15: verificar o critério de balanceamento

Compare as árvores A e B, que contêm as mesmas chaves.

Comparação das mesmas sete chaves: A distribui os nós entre esq e dir; B tem raiz 8 e uma cadeia somente por dir até 32

Considere balanceada uma árvore em que, em cada nó, as alturas das subárvores esquerda e direita diferem no máximo em 1.

Classifique A e B como balanceada ou não balanceada e justifique.

Solução

Explicação

O critério precisa valer em todos os nós. Em A, nenhum nó viola o limite. Em B, a diferença de alturas na raiz já permite rejeitar o balanceamento.

Objetivo de aprendizagem: aplicar o critério de balanceamento em toda a árvore e reconhecer uma violação local suficiente para rejeitá-lo.

Resposta esperada

A é balanceada. B não é balanceada. As alturas justificam as classificações:

ÁrvoreNós verificadosAltura de esqAltura de dirDiferença
A20220
A12 e 28110
AFolhas 8, 16, 24 e 32000
B8066

Em A, todos os nós atendem ao limite. Em B, a diferença 6 na raiz 8 ultrapassa 1.

Exercício 16: comparar os maiores caminhos de busca

Observe as árvores A e B. Cada seta entre dois nós representa uma aresta.

Comparação das mesmas sete chaves: A distribui os nós entre esq e dir; B tem raiz 8 e uma cadeia somente por dir até 32

Compare a busca bem-sucedida mais longa em A e B, começando na raiz.

Para cada árvore, informe: altura em níveis, nós examinados e arestas percorridas.

Solução

Explicação

O maior caminho de uma busca bem-sucedida chega a uma folha no nível mais profundo. A visita inicial à raiz conta como um nó examinado. As arestas contam somente as descidas entre nós, por isso há uma aresta a menos que nós no caminho.

Objetivo de aprendizagem: relacionar altura ao limite da busca, distinguindo nós examinados de arestas percorridas.

Resposta esperada

A tabela compara o caminho mais longo de busca bem-sucedida em cada árvore.

ÁrvoreAlturaNós examinados, no máximoArestas percorridas, no máximo
A332
B776

Exemplos que alcançam esses limites: 20 → 12 → 8 em A e 8 → 12 → 16 → 20 → 24 → 28 → 32 em B.