Dando continuidade ao nosso Curso de Programação C e ao estudo das árvores binárias de busca, vamos aprender nesta aula como remover um nó folha de uma árvore binária.
Quando falamos em remover um nó em uma árvore binária é preciso atenção. Um nó pode ter zero filhos (nó folha), um filho ou ainda dois filhos e cada situação precisa ser tratada de forma individual, afinal não podemos perder nenhum nó durante o processo de remoção. Após a remoção nossa árvore binária precisa continuar consistente, ou seja, com todos os elementos exceto o elemento removido e com os menores à esquerda e os maiores à direita.
Nesta aula vamos tratar apenas a remoção de nós folhas, nós que não possuem filhos. Nas aulas seguintes veremos como remover nós que possuem um e dois filhos.
A função de remoção de nós será uma função recursiva e receberá como parâmetro o ponteiro para a raiz da árvore e o valor a ser removido. Assim, caso o valor a ser removido não exista na árvore, em algum momento a função receberá um ponteiro nulo. Este é o primeiro teste que faremos. Se a raiz recebida for nula, significa que percorremos a árvore e não encontramos o valor procurado, então retornaremos nulo, o que indica que o valor não foi encontrado.
Por outro lado, se a raiz não é nula, então precisamos verificar se ela possui o valor a ser removido. Como dito anteriormente, nesta aula vamos tratar apenas a remoção de nós folhas. Assim, se a raiz recebida possui o valor a ser removido, vamos fazer outro teste para descobrir se é um nó folha. Se for um nó folha, não há nenhum filho, então podemos liberar a memória deste nó e retornar NULL. Este retorno é importante pois o nó folha removido era filho de um outro nó. Então o ponteiro deste nó pai precisa receber o valor NULL, indicando que não existe mais aquele filho.
Contudo, se o valor da raiz não for o valor procurado, então precisamos caminhar na árvore para a esquerda, se o valor procurado for menor, ou para a direita caso contrário. Observe que o ponteiro à esquerda ou à direita recebe o retorno da chamada recursiva, que irá atualizar o ponteiro do nó pai caso algum de seus filhos seja removido.
/*
função para remover nós da Árvore binária
*/
NoArv* remover(NoArv *raiz, int chave) {
if(raiz == NULL){
printf("Valor nao encontrado!\n");
return NULL;
} else { // procura o nó a remover
if(raiz->valor == chave) {
// remove nós folhas (nós sem filhos)
if(raiz->esquerda == NULL && raiz->direita == NULL) {
free(raiz);
printf("Elemento folha removido: %d !\n", chave);
return NULL;
}
else{
// remover nós que possuem 1 ou 2 filhos
}
} else {
if(chave < raiz->valor)
raiz->esquerda = remover(raiz->esquerda, chave);
else
raiz->direita = remover(raiz->direita, chave);
return raiz;
}
}
}
Código de exemplo completo em C para testar uma Árvore Binária de Busca
/*
Aula 274: Como remover um nó FOLHA de uma Árvore Binária?
Código escrito por Wagner Gaspar
Setembro de 2021
*/
typedef struct no{
int valor;
struct no *direita, *esquerda;
}NoArv;
NoArv* inserir_versao_1(NoArv *raiz, int num){
if(raiz == NULL){
NoArv *aux = malloc(sizeof(NoArv));
aux->valor = num;
aux->esquerda = NULL;
aux->direita = NULL;
return aux;
}
else{
if(num < raiz->valor)
raiz->esquerda = inserir_versao_1(raiz->esquerda, num);
else
raiz->direita = inserir_versao_1(raiz->direita, num);
return raiz;
}
}
void inserir_versao_2(NoArv **raiz, int num){
if(*raiz == NULL){
*raiz = malloc(sizeof(NoArv));
(*raiz)->valor = num;
(*raiz)->esquerda = NULL;
(*raiz)->direita = NULL;
}
else{
if(num < (*raiz)->valor)
inserir_versao_2(&((*raiz)->esquerda), num);
else
inserir_versao_2(&((*raiz)->direita), num);
}
}
void inserir_versao_3(NoArv **raiz, int num){
NoArv *aux = *raiz;
while(aux){
if(num < aux->valor)
raiz = &aux->esquerda;
else
raiz = &aux->direita;
aux = *raiz;
}
aux = malloc(sizeof(NoArv));
aux->valor = num;
aux->esquerda = NULL;
aux->direita = NULL;
*raiz = aux;
}
NoArv* buscar_versao_1(NoArv *raiz, int num){
if(raiz){
if(num == raiz->valor)
return raiz;
else if(num < raiz->valor)
return buscar_versao_1(raiz->esquerda, num);
else
return buscar_versao_1(raiz->direita, num);
}
return NULL;
}
NoArv* buscar_versao_2(NoArv *raiz, int num){
while(raiz){
if(num < raiz->valor)
raiz = raiz->esquerda;
else if(num > raiz->valor)
raiz = raiz->direita;
else
return raiz;
}
return NULL;
}
int altura(NoArv *raiz){
if(raiz == NULL){
return -1;
}
else{
int esq = altura(raiz->esquerda);
int dir = altura(raiz->direita);
if(esq > dir)
return esq + 1;
else
return dir + 1;
}
}
int quantidade_nos(NoArv *raiz){
if(raiz == NULL)
return 0;
else
return 1 + quantidade_nos(raiz->esquerda) + quantidade_nos(raiz->direita);
// operador ternário
//return (raiz == NULL)? 0: 1 + quantidade_nos(raiz->esquerda) + quantidade_nos(raiz->direita);
}
int quantidade_folhas(NoArv *raiz){
if(raiz == NULL)
return 0;
else if(raiz->esquerda == NULL && raiz->direita == NULL)
return 1;
else
return quantidade_folhas(raiz->esquerda) + quantidade_folhas(raiz->direita);
}
// função para remover nós da Árvore binária
NoArv* remover(NoArv *raiz, int chave) {
if(raiz == NULL){
printf("Valor nao encontrado!\n");
return NULL;
} else { // procura o nó a remover
if(raiz->valor == chave) {
// remove nós folhas (nós sem filhos)
if(raiz->esquerda == NULL && raiz->direita == NULL) {
free(raiz);
printf("Elemento folha removido: %d !\n", chave);
return NULL;
}
else{
// remover nós que possuem 1 ou 2 filhos
}
} else {
if(chave < raiz->valor)
raiz->esquerda = remover(raiz->esquerda, chave);
else
raiz->direita = remover(raiz->direita, chave);
return raiz;
}
}
}
void imprimir_versao_1(NoArv *raiz){
if(raiz){
printf("%d ", raiz->valor);
imprimir_versao_1(raiz->esquerda);
imprimir_versao_1(raiz->direita);
}
}
void imprimir_versao_2(NoArv *raiz){
if(raiz){
imprimir_versao_2(raiz->esquerda);
printf("%d ", raiz->valor);
imprimir_versao_2(raiz->direita);
}
}
int main(){
NoArv *busca, *raiz = NULL;
int opcao, valor;
do{
printf("\n\t0 - Sair\n\t1 - Inserir\n\t2 - Imprimir\n\t3 - Buscar\n\t4 - Altura\n\t5 - Tamanho\n\t6 - Folhas\n\t7 - Remover\n");
scanf("%d", &opcao);
switch(opcao){
case 1:
printf("\n\tDigite um valor: ");
scanf("%d", &valor);
//raiz = inserir_versao_1(raiz, valor);
//inserir_versao_2(&raiz, valor);
inserir_versao_3(&raiz, valor);
break;
case 2:
printf("\n\tPrimeira impressao:\n\t");
imprimir_versao_1(raiz);
printf("\n");
printf("\n\tSegunda impressao:\n\t");
imprimir_versao_2(raiz);
printf("\n");
break;
case 3:
printf("\n\tDigite o valor a ser procurado: ");
scanf("%d", &valor);
//busca = buscar_versao_1(raiz, valor);
busca = buscar_versao_2(raiz, valor);
if(busca)
printf("\n\tValor encontrado: %d\n", busca->valor);
else
printf("\n\tValor nao encontrado!\n");
break;
case 4:
printf("\n\tAltura da arvore: %d\n\n", altura(raiz));
break;
case 5:
printf("\n\tQuantidade de nos: %d\n", quantidade_nos(raiz));
break;
case 6:
printf("\n\tQuantidade folhas: %d\n", quantidade_folhas(raiz));
break;
case 7:
printf("\t");
imprimir_versao_2(raiz);
printf("\n\tDigite o valor a ser removido: ");
scanf("%d", &valor);
raiz = remover(raiz, valor);
break;
default:
if(opcao != 0)
printf("\n\tOpcao invalida!!!\n");
}
}while(opcao != 0);
return 0;
}
