Carregando...
Carregando...
Ajude a melhorar a plataforma
Considere o seguinte trecho de código que define um método destroyTree para destruir uma árvore binária utilizando caminhamento pós-ordem:
void destroyTree(Node* node) {
if (node == nullptr) {
return;
}
destroyTree(node->left);
destroyTree(node->right);
std::cout << "Deletando nó com valor: " << node->data << std::endl;
delete node;
}
Com base no código acima, qual das alternativas a seguir apresenta a ordem nas quais os nós são deletados:
Explique melhor esta questão
Abre o Tutor com o enunciado e as alternativas já no campo — você revisa e envia.
Esta questão foi verificada por um de nossos administradores.
Alternativa E - Os nós são deletados na ordem de visita: subárvore esquerda, subárvore direita, nó atual.
A gestão de memória é um aspecto fundamental no desenvolvimento de software, especialmente ao lidar com estruturas de dados dinâmicas como as árvores binárias. Quando uma árvore não é mais necessária, seus nós devem ser explicitamente liberados para evitar vazamentos de memória. A forma como essa liberação é realizada é crucial, e a escolha do caminhamento (ou travessia) da árvore desempenha um papel determinante para garantir a integridade da memória e a correta desalocação de todos os recursos.
Uma árvore binária é uma estrutura de dados hierárquica onde cada nó tem, no máximo, dois filhos: um filho à esquerda e um filho à direita. O processo de visitar todos os nós de uma árvore é conhecido como caminhamento ou travessia da árvore. Existem três tipos principais de caminhamentos em profundidade:
O método destroyTree fornecido no enunciado utiliza uma abordagem recursiva para liberar a memória alocada para os nós da árvore. A ordem em que as operações de delete são realizadas é determinada pela sequência das chamadas recursivas e da operação de desalocação dentro da função.
Analisando o código:
void destroyTree(Node* node) {
if (node == nullptr) { // 1. Caso base: se o nó é nulo, retorna.
return;
}
destroyTree(node->left); // 2. Chamada recursiva para a subárvore esquerda.
destroyTree(node->right); // 3. Chamada recursiva para a subárvore direita.
std::cout << "Deletando nó com valor: " << node->data << std::endl; // 4. Impressão (visita).
delete node; // 5. Deleta o nó atual.
}
A sequência de operações (2, 3, 5) define o padrão de caminhamento. Primeiro, a função é chamada para o filho esquerdo (node->left), o que significa que toda a subárvore esquerda será processada antes de qualquer outra coisa no contexto do nó atual. Em seguida, a função é chamada para o filho direito (node->right), processando toda a subárvore direita. Somente depois que ambas as subárvores (esquerda e direita) foram completamente destruídas e seus nós liberados é que o delete node; é executado para o nó atual. Esta ordem de execução corresponde exatamente ao caminhamento pós-ordem: Esquerda, Direita, Nó.
A escolha do caminhamento pós-ordem para destruir uma árvore é essencial. Se um nó fosse deletado antes de seus filhos (como em pré-ordem), os ponteiros para node->left e node->right se tornariam ponteiros pendentes (dangling pointers) após a desalocação do nó pai, levando a comportamentos indefinidos, erros de segmentação ou vazamentos de memória, pois os nós filhos nunca seriam alcançados e liberados.
Vamos analisar as alternativas com base no comportamento do código:
| Ordem Proposta | Corresponde ao Código? | Motivo da Correção/Incorreção |
| :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| (A) subárvore direita, subárvore esquerda, nó atual | Não | O código primeiro processa a subárvore esquerda (destroyTree(node->left)) e depois a subárvore direita (destroyTree(node->right)). A ordem das chamadas recursivas é fundamental. |
| (B) subárvore direita, nó atual, subárvore esquerda | Não | A ordem das chamadas recursivas está invertida para as subárvores e a desalocação do nó atual ocorreria antes da subárvore esquerda, o que é problemático. |
| (C) nó atual, subárvore esquerda, subárvore direita | Não (Este seria o caminhamento pré-ordem) | Se o nó atual fosse deletado primeiro (delete node; antes das chamadas recursivas), tentar acessar node->left ou node->right em seguida resultaria em um erro, pois o nó já estaria liberado. Isso causaria comportamento indefinido ou crash do programa, além de vazamento de memória se as subárvores nunca fossem alcançadas. |
| (D) nó atual, subárvore direita, subárvore esquerda | Não | Similar à alternativa (C), deletar o nó atual primeiro é um erro grave para a destruição da árvore. Além disso, a ordem das subárvores está invertida em relação ao que o código faria se fosse um pré-ordem modificado. |
| (E) subárvore esquerda, subárvore direita, nó atual | Sim | O código destroyTree(node->left); é executado primeiro, garantindo que toda a subárvore esquerda seja destruída. Em seguida, destroyTree(node->right); é executado, destruindo a subárvore direita. Somente após esses dois processos serem concluídos é que delete node; é invocado para o nó atual. Essa é a definição exata de caminhamento pós-ordem e a ordem correta para a desalocação segura de uma árvore binária. |
O método destroyTree implementa a destruição de uma árvore binária utilizando o caminhamento pós-ordem. Essa escolha é deliberada e tecnicamente necessária para garantir que todos os nós descendentes sejam liberados da memória antes que o nó pai seja desalocado. Ao seguir a ordem "subárvore esquerda, subárvore direita, nó atual", evita-se o problema de ponteiros pendentes e garante-se que cada alocação de memória seja correspondida por uma desalocação, prevenindo vazamentos e erros de acesso à memória.
Alternativa E.