Você está na página 1de 43

rvore AVL

(Em linguagem de programao C)


Kevim Brasil Maciel
Graduando em Cincia da Computao
Manaus/AM 2013

rvore AVL um tipo de armazenamento (organizao) de dados em memoria
interna. Essa rvore possu como base o mesmo modelo da arvore binria, em que
uma rvore dividida em duas sub-rvores e essas sub-rvores so subdivididas em
outras sub-rvores, assim sucessivamente.
O nome AVL vem de seus criadores Adelson Velsky e Landis. A rvore
considerada balanceada quando a diferena entre a altura das sub-rvores esquerda
e direita forem iguais a 1, 0 ou -1. O fator de balanceamento nada mais que o
calculo da altura da sub-rvore direita menos a altura da sub-rvore esquerda.
Abordaremos separadamente as partes mais fundamentais da rvore AVL para
o melhor entendimento e depois de todos os passos veremos a rvore completa. A
arvore AVL auto-balanceada, ou seja, ela capaz de se balancear quando
necessrio. Dividiremos ento a rvore da seguinte forma:
1. Registro;
2. Main (Programa Principal);
3. Insero;
4. Rotao:
A Esquerda;
A Direita;
5. Funo de Balanceio;
6. Remoo;
7. Impresso:
Em Ordem;
Pr-Ordem;
Ps-Ordem;
8. Fator de Balanceamento e Altura da Arvore;
9. Destruio da rvore AVL;
Vale a pena ressaltar que para melhor compreenso necessrio que voc j
conhea a rvore binria, e que tenha um conhecimento na linguagem de
programao C. No copie trabalhos prontos da internet, pois voc no aprender
nada. Bons estudos!!!!!!
1.0 Registro

O registro a criao do tipo abstrato de dado, nele voc colocar os campos
necessrios para o seu programa, lembre-se que o registro depender do que lhe foi
solicitado para a construo do algoritmo.
Neste exemplo trabalharemos somente com um dado chave, um ponteiro do
prprio tipo abstrato de dado criado, ou seja, do seu prprio registro, um para a
esquerda e um para a direita, e claro o fundamental para que sua rvore seja uma
rvore AVL, um campo para fator de balanceamento.
O campo para a chave e o fator de balanceamento ser do tipo inteiro.
Lembrando novamente que a os campos variam de acordo com a necessidade.
Observe a criao do Registro:
typedef struct noh{
int num;
int fb;
struct noh *esq;
struct noh *dir;
}noh;
Este cdigo acima o registro, nele voc especificou quais campos cada n da
sua rvore possuir. Voc deve ter notado que antes da declarao do ponteiro
utilizada uma parte da declarao do registro struct noh *dir, isso significa que
esses ponteiros iro apontar (guardar um endereo de memoria) um n do mesmo
tipo que ele. A imagem a seguir demonstra isso.








2.0 Main
Chave
Fator de balanceamento
Ponteiros
noh
Numero (chave)
Fator de Balanceamento
Ponteiro para a esquerda e direita
noh noh
Ponteiro do n que aponta para a
direita do mesmo tipo que ele
Como estamos montando uma rvore AVL em linguagem C, faz-se
fundamental a utilizao da Main. Voc tem duas opes em relao ao que fazer
com ela, voc pode somente criar o ponteiro para rvore AVL, ou ento, criar seu
menu nela, neste caso trabalharemos com a segunda opo.
Passos fundamentais: cria-se o ponteiro para a rvore AVL, inicializa-se ela
com o valor nulo (NULL), a parti da fica a seu critrio a construo do menu do
seu programa.
Observe a MAIN:
void main(){
noh *avl=NULL;
int num,op;
do{
printf("\n\t\t Menu \n");
printf("\t\t 1 - Inserir \n");
printf("\t\t 2 - Imprimir em Ordem \n");
printf("\t\t 3 - Imprimir em Pre-Ordem \n");
printf("\t\t 4 - Imprimir em Pos-Ordem \n");
printf("\t\t 5 - Excluir \n");
printf("\t\t 6 - Altura \n");
printf("\t\t 7 - Destroi \n");
printf("\t\t 8 - Sair \n");
printf("\nOpcao: ");
scanf("%d",&op);
switch(op){



case 1: printf("Numero: ");
scanf("%d",&num);
avl=insere(avl,num);
break;
Criao do ponteiro para a AVL
Leitura da opo do usurio
E
x
i
b
i

o

d
o

M
e
n
u

Verifica qual a opo escolhida pelo usurio

case 2: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Ordem:\n");
in_ordem(avl);
break;

case 3: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Pre - Ordem:\n");
pre_ordem(avl);
break;

case 4: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Pos - Ordem:\n");
pos_ordem(avl);
break;

case 5: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
printf("Numero: ");
scanf("%d",&num);
avl=excluir(avl,num);
printf("Exclusao Sucedida!\n");
break;

case 6: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
num=alt(avl);
printf("Altura da Arvore: %d\n",num);
break;


case 7: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
avl=destroi_avl(avl);
printf("Exclusao Concluida\n");
break;

case 8: exit(0);

default: printf("Opo Invalida!\n");
}
getch();
system("cls");
}while(op!=8);
}
Esse procedimento consiste, na criao do ponteiro, exibio do menu e
leitura da opo desejada. Ele testa qual a opo por ele desejada e ento chama
a funo correspondente ao requerimento, caso a opo inserida no seja
nenhuma apresentada pelo programa ele mostra uma mensagem de erro e torna
a pedir a opo ao usurio.
Caso voc ache estranho este trecho system("cls"); ele serve para
limpar a tela do usurio, vale ressaltar que ele no funciona na plataforma Linux,
ele foi especialmente desenvolvido para Windows. H tambm um caso acima um
pouco diferente no teste da varivel nula; esse teste if(!avl) equivalente a
if(avl==NULL).
Observe que antes da chamada da funo ele verifica se a arvore nula,
se for, ele no perde tempo chamando a funo e volta ao menu, aumentado
assim a funcionalidade do programa.



3.0 Insero


A funo de insero similar a funo de insero da rvore binria, s
so incrementados os campos de atualizao do fator de balanceamento e
rotao da rvore caso se faa necessrio; a funo de rotao separada, ou
seja, ela chamada dentro desta funo, porm incrementada em outra parte
do programa.
Observe a funo:
noh *insere(noh *raiz,int num){
noh *novo=NULL;
if(!raiz){
novo=(noh *)malloc(sizeof(noh));
if(!novo){
printf("ERRO de alocao de memoria\n");
getch();
exit(1);
}
Recebimento do ponteiro de AVL e do
numero ser inserido ambos por parmetro
Verifica se a arvore vazia
Verifica se o
espao em
memoria foi
reservado

novo->esq=NULL;
novo->dir=NULL;
novo->num=num;
novo->fb=0;
printf("Insercao Concluida!\n");
return novo;
}else{
if(raiz->num==num){
printf("Numero ja consta, nao %c necessario
inseri-lo novamente!\n\n",130);
return(raiz);
}
if(num < raiz->num ){
raiz->esq=insere(raiz->esq,num);
}else{
raiz->dir=insere(raiz->dir,num);
}
raiz->fb=fb(raiz);
raiz=bl(raiz);
return raiz;
}
}
Essa funo recebe por parmetro um ponteio para a AVL e o valor a ser
inserido, caso a arvore esteja vazia ela aloca um espao em memoria, e testa se
ele realmente foi alocado, caso no tenha sido ele fora a sada do programa,
depois de testar ele inicializa todos os campos, o fator de balanceamento
iniciado com 0 porque independente de que lugar ele ser inserido, ele ser uma
folha, ou seja ele no ter filhos.
Caso a arvore no esteja vazia, ele verifica se o numero a ser inserido
menor ou maior que a raiz, se o numero for menor, ele de forma recursiva o envia
Inicializa os campos no novo n
Verifica se o
numero j
existe
Verifica para qual lado da
arvore esse numero ser
enviado
Atualiza o fator de
balanceamento e
chama a funo
que balanceia a
arvore
mais a esquerda da rvore, caso seja maior, ele de forma recursiva o envia mais
a direita da rvore.
Observe a imagem:








Ele percorrer cada n da arvore fazendo esse mesmo teste, at encontrar
o local apropriado que satisfaa a condio. Depois de inserido atualizado o
fator de balanceamento da raiz de todas as razes a parti de onde foi inserido o
novo elemento, depois de atualizado o fator de balanceamento ele chama a
funo de balanceio do arvore (bl), caso seja necessrio ele se encarrega da
rotao da arvore, lembrando que s necessrio rotacionar quando o fator de
balanceamento da raiz principal for 2 ou -2, caso seja 1,0 ou -1 sua arvore
considerada balanceada. A funo acima no insere numero repetidos, mas isso
fica a critrio do programador inseri-lo ou no.
Vamos acompanhar o exemplo de uma rvore AVL que tenha como raiz o
numero 8, e na sub-rvore esquerda possua os nmeros 3, 4 e 5 e na direita o
numero 9, e vamos inserir o numero 10 para compreender como funciona a
insero.







8
4
3 5
9
Raiz
Primeiramente acompanharemos o primeiro teste da funo, verificar se a
raiz nula, se for deve-se aloca um espao em memoria e testar para ter certeza
que a memoria foi alocado, se ele foi alocado inicializam-se os seus campos:
if(!raiz){
novo=(noh *)malloc(sizeof(noh));
if(!novo){
printf("ERRO de alocao de memoria\n");
getch();
exit(1);
}

novo->esq=NULL;
novo->dir=NULL;
novo->num=num;
novo->fb=0;
printf("Insercao Concluida!\n");
return novo;
}
}
Na nossa arvore-exemplo nossa raiz no nula, pois existe um valor e o
numero 8. Ento continuamos os testes.
Segundo teste: verificar se o numero da raiz igual ao numero a ser
inserido, se for ele mostra uma mensagem para o usurio e sai da funo.
if(raiz->num==num){
printf("Numero ja consta, nao %c necessario
inseri-lo novamente!\n\n",130);
return(raiz);
}
Aloca um espao
em memoria
Teste para ter
certeza de que o
espao em
memoria foi
alocado
Inicializao dos campos do
novo n
No caso da nossa arvore o numero que queremos inserir diferente da
raiz, ento continuamos a execuo dos testes da funo.
Terceiro teste: verificar se o numero a ser inserido menor ou maior
que a raiz.
if(num < raiz->num ){
raiz->esq=insere(raiz->esq,num);
}else{
raiz->dir=insere(raiz->dir,num);
}

No nosso caso o numero que queremos inserir 10 e a raiz 8 ento o
programa chama a funo enviando-a para a direita da arvore. Observe a
demonstrao no desenho.
raiz=inserir(raiz->dir,num);







A raiz passou a ser o numero 9 porque quando a funo chamou a si
prpria ela enviou o endereo do elemento da direita, caso o numero fosse menor
ele teria sido enviado esquerda enviando o endereo do ponteiro do elemento
da esquerda. No se preocupe em perder o ponteiro, como trabalhamos com
recurso o programa volta raiz automaticamente.
A parti da faz-se novamente todos os testes acima, como o numero 10
maior que o numero 9 ele ser inserido direita do n (1 passo). Observe na
imagem:


8
4
3 5
9
Raiz
Ponteiro para a esquerda
Ponteiro para a direita






Toda vez em que o fator de balanceamento da raiz principal for 2 ou -2 sua
arvore esta desbalanceada e voc precisa fazer uma serie de rotaes para
balanceada. Alguns autores utilizam quatro rotaes, mas na verdade so apenas
duas e elas sero explicadas mais adiante, so elas: rotao esquerda e
rotao direita.

4.0 Rotaes


Como j se foi citado acima, alguns professores e autores consideram
quatro rotaes rotao: simples esquerda, rotao simples direita, rotao
dupla esquerda e rotao dupla direita, mas essas duas ultimas so duas
rotaes simples em cada, logo trabalharemos somente com rotaes simples e
mostraremos como funciona as rotaes duplas.
Toda e qualquer vez que o fator de balanceamento da raiz principal for 2 ou
-2 voc tem por obrigao rotacionar a arvore.
Vamos visualizar o cdigo das rotaes simples esquerda e direita e
em seguida aprenderemos identificar qual rotao necessria para balancear a
arvore.

Rotao simples esquerda:

noh *rot_esq(noh *raiz){
noh *aux=NULL;
aux=raiz->dir;
raiz->dir=aux->esq;
8
4
3 5
9
Raiz
10
Raiz no campo
direita recebe o
valor novo
Ponteiro de AVL
Varivel auxiliar
aux->esq=raiz;
return aux;
}
Suponhamos que tenhamos um arvore desbalanceada direita. Observe a
imagem que demonstra uma rotao simples esquerda.






O primeiro passo criar uma varivel auxiliar: noh *aux=NULL;, depois
a varivel auxiliar receber o endereo de memoria do filho da direita da raiz:
aux=raiz->dir;.








Em seguida ao invs de apontar para o endereo de memoria do numero 3,
a raiz apontar para o filho da esquerda do auxiliar: raiz->dir=aux->esq;.







1
3
4
Fator de balanceamento=2
Fator de balanceamento=1
Fator de balanceamento=0
1
3
4
Raiz
auxiliar
2
2
1
3
4
Raiz
auxiliar
2
Ps isso, ao invs de o auxiliar apontar para seu filho da esquerda ele ir
apontar para a raiz: aux->esq=raiz.








A ultima ao do programa ser retornar o valor da varivel auxiliar:
return aux;. A arvore agora balanceada fica assim:









Rotao simples direita:

noh *rot_dir(noh *raiz){
noh *aux=NULL;
aux=raiz->esq;
raiz->esq=aux->dir;
aux->dir=raiz;
return aux;
}
Ponteiro de AVL
Varivel auxiliar
1
3
4
Raiz
auxiliar
2
1
3
Raiz
2
4
Fator de balanceamento= -1
Fator de balanceamento= 1
Fator de balanceamento= 0
Fator de balanceamento= 0
Agora veremos um exemplo de uma arvore desbalanceada a esquerda e
vamos fazer uma rotao simples a direita.







O primeiro passo criar uma varivel auxiliar: noh *aux=NULL;, depois
a varivel auxiliar receber o endereo de memoria do filho da esquerda da raiz:
aux=raiz->esq;.








Em seguida ao invs de apontar para o endereo de memoria do numero 2,
a raiz apontar para o filho da direita do auxiliar: raiz->esq=aux->dir;.







Ps isso, ao invs de o auxiliar apontar para seu filho da direita ele ir
apontar para a raiz: aux->dir=raiz.
1 3
4
2
Fator de balanceamento= 1
Fator de balanceamento= 0
Fator de balanceamento= -2
Fator de balanceamento= 0
1 3
4
2
Raiz
Auxiliar
1 3
4
2
Raiz
Auxiliar






A ultima ao do programa ser retornar o valor da varivel auxiliar:
return aux;. A arvore agora balanceada fica assim:









Vamos analisar alguns casos especficos, em que se faz necessrio usar
uma rotao dupla.
Toda e qualquer vez que o fator de balanceamento for 2 ou -2 e o fator de
balanceamento do filho h qual a arvore esta desiquilibrada for diferente do sinal
da raiz.
Fb(raiz)= 2 e Fb(direita) = 1 -> Rotao simples esquerda.
Fb(raiz)= -2 e Fb(direita) = -1 -> Rotao simples direita.
Fb(raiz)= 2 e Fb(direita) = -1 -> Rotao dupla esquerda.
Fb(raiz)= -2 e Fb(direita) = 1 -> Rotao dupla direita.

Se o fator de balanceamento da raiz for igual 2 e o fator de
balanceamento o filho da direita for igual a -1 significa que voc precisa fazer uma
rotao a esquerda no filho da direita e depois fazer uma rotao simples a
direita na raiz. A imagem abaixo demonstra isso:
1 3
4
2
Raiz
Auxiliar
1
3
4
2
Fator de balanceamento= 0
Fator de balanceamento= -1
Fator de balanceamento= 0
Fator de balanceamento= -1







Para resolver isso fazemos uma rotao esquerda no filho da direita.
Para entender o que aconteceu analise os exemplos anteriores. Depois da
rotao a arvore ficou assim:








Depois disso basta fazer uma rotao direita na raiz.






Simples assim. Quando o fator de balanceamento da raiz for igual a -2 e o
fator de balanceamento do filho da esquerda for igual a 1, necessrio fazer uma
rotao a esquerda no filho da esquerda e uma rotao simples a direita na raiz.
A imagem seguir demonstra isso exemplo.


3
4
2
Raiz
Fator de balanceamento =2
Filho da direita
Fator de balanceamento = -1
3
4
2
3
4
2
Rotao simples direita





Para resolver isso fazemos uma rotao direita no filho da esquerda.
Para entender o que aconteceu analise os exemplos anteriores. Depois da
rotao a arvore ficou assim:






Depois disso basta fazer uma rotao direita na raiz.






5.0 Funo de Balanceio

Essa funo responsvel pelo controle das rotaes na AVL, como todas
nossas funes, ela receber o ponteiro para AVL por parmetro, e a parti dos
testes do fator de balanceamento da raiz e de seus filhos realizar as rotaes
necessrias.
Funo de balanceio:
noh *bl(noh *raiz){
if(raiz->fb == -2){
if(raiz->esq->fb == 1)
raiz->esq=rot_dir(raiz->esq);
3
4
2
Filho da esquerda
Fator de balanceamento = 1
Raiz
Fator de balanceamento = -2
Rotao simples esquerda
3
4
2
3
4
2
raiz=rot_esq(raiz);
}else
if(raiz->fb == 2){
if(raiz->dir->fb == -1)
raiz->dir=rot_esq(raiz->dir);
raiz=rot_esq(raiz);
}
return (raiz);
}

Essa funo testa primeiramente o fator de balanceamento da raiz para ver
se h necessidade de rotacionar a arvore, caso o fator de balanceamento for
diferente de 2 ou -2 a funo no executa. Depois de verificar e encontrar a
necessidade de uma rotao, ele testa tambm se necessrio um rotao
dupla. Quando h necessidade de uma rotao dupla? Todas vez que o sinais
dos fatores de balanceamento forem diferentes, a funo analisa e faz as
rotaes

6.0 Remoo


A funo de remoo tambm consiste no recebimento por paramento do
ponteiro da AVL e o numero a ser removido. Essa funo faz uma serie de teste
at encontrar o numero, depois que o encontra, torna a fazer vrios testes ate que
uma das condies seja satisfeita.
H quatro casos que ocorrem quando queremos remover um elemento da
rvore.
Seus filhos podem ser nulos








Numero a ser excludo
Observe que ele no
tem filhos
Para esse caso basta liberar o endereo de memoria do elemento e
retornar o valor nulo (NULL);
if (raiz->esq==NULL && raiz->dir==NULL){
free(raiz);
return NULL;
}
Observe passo a passo:








Libera-se o endereo de memoria do elemento free(raiz); e
retorna-se o valor nulo (NULL) return NULL;.








Pode conter o filho da esquerda






Numero a ser excludo
Observe que ele possui
somente o filho da esquerda
Numero a ser excludo
O numero foi removido e retornado o valor nulo
Para esse caso especial, inicialmente precisa confirma se ele no possui
filho a direita, se ele no possui, uma varivel auxiliar recebe o valor do filho da
esquerda, libera-se a memoria do elemento a ser removido e ento retornado o
valor da varivel auxiliar.
if(raiz->dir==NULL){
aux=raiz->esq;
free(raiz);
return aux;
}
Observe passo a passo:








A varivel auxiliar recebe o valor do filho da esquerda aux=raiz->esq;







Libera-se o endereo de memoria do elemento a ser removido
free(raiz); e retorna o valor da varivel auxiliar return aux;

Numero a ser excludo

No possui filho a direita
Numero a ser excludo

No possui filho a direita
Auxiliar







A arvore fica da seguinte forma aps a remoo:






Pode conter o filho da direita.
Esse processo o inverso do caso anterior. Primeiramente confirma se ele
no possui filho a esquerda, se ele no possui, uma varivel auxiliar recebe o
valor do filho da direita, libera-se a memoria do elemento a ser removido e ento
retornado o valor da varivel auxiliar.
if(raiz->esq==NULL){
aux=raiz->dir;
free(raiz);
return aux;
}
Observe passo a passo:





Numero a ser excludo
Raiz

Auxiliar
Antiga varivel auxiliar
Numero a ser excludo

No possui filho a esquerda
A varivel auxiliar recebe o valor do filho da direita aux=raiz->dir;







Libera-se o endereo de memoria do elemento a ser removido
free(raiz); e retorna o valor da varivel auxiliar return aux;









A arvore fica da seguinte forma aps a remoo:






Pode conter os dois filhos
Numero a ser excludo

Auxiliar
No possui filho a esquerda
Numero a ser excludo
Raiz

Auxiliar
No possui filho a esquerda
Antiga varivel auxiliar
Esse caso requer uma ateno especial, para resolv-lo voc precisar de
uma funo extra que retorne o maior elemento da sub-rvore esquerda ou o
menor elemento da sub-rvore direita. Esse valor que ser devolvido pela funo
ser a nova raiz.
Funo que procura o maior elemento: esta funo analisar toda a sub-
arvore esquerda ate encontrar o maior valor, e ele ser a nova raiz.
noh *maior(noh* raiz){
noh *aux=NULL;
if(raiz->dir==NULL){
return raiz;
}else{
aux=maior(raiz->dir);
raiz->fb=fb(raiz);
return aux;
}
}
Depois que encontramos o maior elemento a parti do numero que
queremos excluir, podemos ento eliminar a raiz desejada.
else{
aux=maior(raiz->esq);
raiz->num=aux->num;
raiz->esq=excluir(raiz->esq,raiz->num);
free(aux);
printf("Exclusao Sucedida!\n");
return raiz;
}
A funo exemplificada ficaria assim:




Numero a ser excludo

Maior numero da esquerda

O valor da raiz substitudo pelo valor do maior elemento raiz-
>num=aux->num;. Depois a funo de forma recursiva ele envia o valor da
raiz da esquerda e o numero do maior elemento a ser excludo. Como a raiz
passou a ter esse valor o n o que ser removido e no a raiz.








Funo com o numero removido:






Depois destes passos, o numero ainda no tenha sido localizado a funo
faz um outro teste: verifica se o numero a ser excludo maior ou menor que a
raiz, como acontece na funo inserir s que ao contrrio. Se o numero for maior
a funo envia o endereo de memoria da direita e o valor a ser removido, caso
seja menor o endereo que enviado o endereo da esquerda.

if(raiz->num < num)
raiz->dir=excluir(raiz->dir,num);
else
raiz->esq=excluir(raiz->esq,num);

Ponteiro para a direita
Ponteiro para a esquerda
Numero a ser removido
Numero a ser excludo
Substitudo pelo maior valor da esquerda

Elimina esse n, pois a
raiz passou a possuir
seu valor

Depois destes testes atualizamos o fator de balanceamento e
chamamos a funo de balanceamento que faz a rotao da arvore caso seja
necessrio.
raiz->fb=fb(raiz); raiz=bl(raiz);
Vejamos a funo completa:


noh *excluir(noh *raiz, int num){
noh *aux=NULL;

if(raiz->num==num){
if (raiz->esq==NULL && raiz->dir==NULL){
free(raiz);
return NULL;
}
else
if(raiz->esq==NULL){
aux=raiz->dir;
free(raiz);
return aux;
}
else
if(raiz->dir==NULL){
aux=raiz->esq;
free(raiz);
return aux;
}else{
aux=maior(raiz->esq);
raiz->num=aux->num;
raiz->esq=excluir(raiz->esq,raiz->num);
free(aux);
return raiz;
Recebimento do ponteiro de AVL e o numero a ser
removido
Verifica se a
raiz contem o
numero a ser
removido
Testa se ele no
possui filhos
Testa se ele no possui
o filho da esquerda
Testa se ele no possui
o filho da direita
Caso ele
tenha
ambos os
filhos
}
}
if(raiz->num < num)
raiz->dir=excluir(raiz->dir,num);
else
raiz->esq=excluir(raiz->esq,num);
raiz->fb=fb(raiz);
return raiz;
}


7.0 Impresso da Arvore
Existem trs mtodos de impresso de rvore AVl que constantemente
utilizamos:
Em Ordem;
Pr-Ordem;
Ps-Ordem.

7.1 Impresso em Ordem
A impresso em ordem percorre primeiramente a sub-rvore esquerda,
imprimindo os nmeros da esquerda, raiz e direita respectivamente e depois vai
para a sub-rvore direita imprimindo sempre nessa ordem.
Ele percorre toda a sub-rvore da esquerda e retorna imprimindo os
valores. Vejamos a imagem que demonstra isso:







Verifica para qual lado da
arvore esse numero
provavelmente est
Atualizao do
fator de
balanceamento
1
2
3
4
5
6
7
A ideia da impresso em ordem permanece a mesma, imprimir o valor da
esquerda, imprimir a raiz e depois o filho da direita.
Suponhamos que nossa arvore possua 3 ns. 1 imprimimos o numero da
esquerda, depois o da raiz e por ultimo o da direita.






A impresso imprime o numero que est na esquerda: 1, depois o valor
da raiz: 2 e por ultimo o da direita: 3. A impresso sairia assim: 1,2 e 3.
O cdigo desta impresso recebe por parmetro o ponteiro pra raiz. Ele
ir imprime enquanto a raiz for diferente do valor nulo (NULL).

void in_ordem(noh *raiz){
if(raiz!=NULL){
in_ordem(raiz->esq);
printf("\t\t%d -> (%d)\n",raiz->num);
in_ordem(raiz->dir);
}
}

7.2 Impresso em Pr-Ordem

A impresso em pr-ordem percorre primeiramente a sub-rvore esquerda,
imprimindo o valor da raiz, esquerda e direita respectivamente e depois vai para a
sub-rvore direita imprimindo sempre nessa ordem.
Ele percorre toda a sub-rvore da esquerda e retorna imprimindo os
valores. Vejamos a imagens que demonstra isso:

2
1
3
Ponteiro da AVL
Esquerda
Raiz
Direita







Suponhamos que nossa arvore seja similar a do exemplo da impresso
em ordem. Primeiro imprimimos a raiz, depois esquerda e direita.







A impresso imprime o numero que est na raiz: 2, depois o valor da
esquerda:1 e por ultimo o da direita: 3. A impresso sairia assim: 2,1 e 3.
O cdigo desta impresso recebe por parmetro o ponteiro pra raiz. Ele ir
imprimir enquanto a raiz for diferente do valor nulo (NULL).

void pre_ordem(noh *raiz){
if(raiz != NULL){
printf("%d -> (%d)\n",raiz->num);
pre_ordem(raiz->esq);
pre_ordem(raiz->dir);
}
}

7.3 Impresso em Ps-Ordem
1
2
3
4
5
6
7
2
1
3
Ponteiro da AVL
Esquerda
Direita
Raiz
A impresso em ps-ordem percorre primeiramente a sub-rvore
esquerda, imprimindo o valor da esquerda, direita e raiz respectivamente e
depois vai para a sub-rvore direita imprimindo sempre nessa ordem.
Ele percorre toda a sub-rvore da esquerda e retorna imprimindo os
valores. Vejamos a imagens que demonstra isso:







Suponhamos que nossa arvore seja similar a do exemplo da impresso em
ordem e pr-ordem. Primeiro imprimimos a esquerda, depois direita e raiz.






A impresso imprime o numero que est na esquerda: 1, depois o valor da
direita: 3 e por ultimo o da raiz: 2. A impresso sairia assim: 1, 3 e 2.
O cdigo desta impresso recebe por parmetro o ponteiro pra raiz. Ele ir
imprime enquanto a raiz for diferente do valor nulo (NULL).

void pos_ordem(noh *raiz){
if(raiz != NULL){
pos_ordem(raiz->esq);
pos_ordem(raiz->dir);
printf("%d -> (%d)\n",raiz->num,raiz->fb);
}
1
2
3
4
5
6
7
2
1
3
Ponteiro da AVL
Direita
Raiz
Esquerda
}

8.0 Fator de Balanceamento e Altura da Arvore

Ao primeiro contato com o fator de balanceamento assusta muitas pessoas e
elas acabam achando que difcil, mas no . O fator de balanceamento nada
mais do que a altura da sub-rvore direita menos a altura da sub-rvore
esquerda.
Observe a funo do calculo do fator de balanceamento:
int fb(noh *raiz){
return(alt(raiz->dir) - alt(raiz->esq));
}
So apenas trs linha de cdigo, mas observe que ele chama a funo que
calcula a altura.

8.1 Altura

A funo altura calcula o tamanho a sub-rvore direita e esquerda,
depois disso ele compara ambas, e devolve a maior +1, esse +1 significa
que ele est acrescentando com o n em que ele est presente.
A funo consiste basicamente no que foi descrito acima, calcula as
duas alturas e as compara. Observe:

int alt(noh *raiz){
int alt_esq,alt_dir;

if(raiz==NULL)
return(0);
else{
alt_esq=alt(raiz->esq);

alt_dir=alt(raiz->dir);

Ponteiro da AVL
Cria as variveis que iro
guardar o valor da direita e
da esquerda
Calcula a altura da sub-rvore esquerda
Calcula a altura da sub-rvore direita
if(alt_esq < alt_dir)
return alt_esq+1;
else
return alt_dir+1;
}
}

Quando um n no possui filhos sua altura zero. E o fator de
balanceamento vai receber o valor da arvore direita menos a esquerda (alt_d
alt_e). Vejamos o exemplo como fator de balanceamento.











Agora vejamos o mesmo exemplo, agora como altura.








Compara e retorna a maior
altura + 1
2
1
3
Como ele no tem filhos seu
fator de balanceamento zero
Nesse caso o
fator de
balanceamento
0, porque o
tamanho da
altura mesmo
(1-1=0)
2
1
3
Esse n tem altura 0, porque
quando um n no possui filhos
funo retorna 0
A altura desse
n 1, como
seus filhos so
iguais, ele
recebe o valor
do maior +1,
seu filho tinha
altura 0, e com
a soma de 1 ele
passa a ter
altura 1

Cuidado! Altura no igual ao fator de balanceamento! A altura
sempre a maior altura +1, j o fator de balanceamento a altura da direita
menos altura da esquerda!

9.0 Destruio da rvore AVL
A destruio da arvore AVL um mtodo para liberar todos os espaos
de memoria reservados pela arvore. Ela serve para voc no precisar reiniciar
seu programa toda a vez que voc utilizar um novo exemplo e claro serve para
liberar todo o espao de memoria reservado.
Esse programa consiste no recebimento do ponteiro da AVL por parmetro,
o processo de excluso similar a impresso ps-ordem, porem onde havia um
printf ser substitudo por free. Ele percorrer a sub-rvore esquerda
eliminando o menor elemento, depois exclui a direita e por ultimo a raiz. O
exemplo abaixo demonstra isso:






Funo:
noh *destroi_avl(noh *raiz){
noh *aux=raiz;
if(aux!=NULL){
destroi_avl(aux->esq);
destroi_avl(aux->dir);
free(raiz);
raiz=NULL;
return(raiz);
}
}
Depois disso sua arvore fica vazia e disponvel para iniciar outra.
2
1
3
1 a ser
removido
2 a ser
removido
3 a ser
removido
Ponteiro da AVL
Esquerda
Direita
Libera Raiz

Cdigo completo

#include<stdio.h>
#include<stdlib.h>

/*****************************************************/
REGISTRO
/*****************************************************/

typedef struct noh{
int num;
int fb;
struct noh *esq;
struct noh *dir;
}noh;

/*****************************************************/
ROTAO DIREITA
/*****************************************************/
noh *rot_dir(noh *raiz){
noh *aux=NULL;
aux=raiz->esq;
raiz->esq=aux->dir;
aux->dir=raiz;
return aux;
}

/*****************************************************/
ROTAO ESQUERDA
/*****************************************************/
noh *rot_esq(noh *raiz){
noh *aux=NULL;
aux=raiz->dir;
raiz->dir=aux->esq;
aux->esq=raiz;
return aux;
}


/******************************************************/
DESTROI ARVORE AVL
/******************************************************/

noh *destroi_avl(noh *raiz){
noh *aux=raiz;
if(aux!=NULL){
destroi_avl(aux->esq);
destroi_avl(aux->dir);
free(raiz);
raiz=NULL;
return(raiz);
}
}

/*****************************************************/
BALANCEA
/*****************************************************/
noh *bl(noh *raiz){
if(raiz->fb==-2){
if(raiz->esq->fb==1)
raiz->dir=rot_dir(raiz->esq);
raiz=rot_esq(raiz);
}else
if(raiz->fb==2){
if(raiz->dir->fb==-1)
raiz->esq=rot_esq(raiz->dir);
raiz=rot_esq(raiz);
}
return (raiz);
}

/******************************************************/
FATOR DE BALANCEAMENTO
/******************************************************/
int fb(noh *raiz){
return(alt(raiz->dir) - alt(raiz->esq));
}

/******************************************************/
ALTURA DA ARVORE
/******************************************************/
int alt(noh *raiz){
int alt_esq,alt_dir;

if(raiz==NULL)
return(0);
else{
alt_esq=alt(raiz->esq);

alt_dir=alt(raiz->dir);

if(alt_esq < alt_dir)
return alt_esq+1;
else
return alt_dir+1;
}
}



/*****************************************************/
MAIOR DA ESQUERDA
/*****************************************************/
noh *maior(noh* raiz){
noh *aux=NULL;
if(raiz->dir==NULL){
return raiz;
}else{
aux=maior(raiz->dir);
raiz->fb=fb(raiz);
return aux;
}
}
/******************************************************/
EXCLUIR
/******************************************************/
noh *excluir(noh *raiz, int num){
noh *aux=NULL;

if(raiz->num==num){
if (raiz->esq==NULL && raiz->dir==NULL){
free(raiz);
return NULL;
}
else
if(raiz->esq==NULL){
aux=raiz->dir;
free(raiz);
return aux;
}
else
if(raiz->dir==NULL){
aux=raiz->esq;
free(raiz);
return aux;
}else{
aux=maior(raiz->esq);
raiz->num=aux->num;
raiz->esq=excluir(raiz->esq,raiz->num);
free(aux);
return raiz;
}

}
if(raiz->num < num)
raiz->dir=excluir(raiz->dir,num);
else
raiz->esq=excluir(raiz->esq,num);
raiz->fb=fb(raiz);
raiz=bl(raiz);
return raiz;
}

/******************************************************/
IMPRIME EM POS-ORDEM
/******************************************************/
void pos_ordem(noh *raiz){
if(raiz != NULL){
pos_ordem(raiz->esq);
pos_ordem(raiz->dir);
printf("%d \n",raiz->num);
}
}


/******************************************************/
IMPRIME EM PRE-ORDEM
/******************************************************/
void pre_ordem(noh *raiz){
if(raiz != NULL){
printf("%d \n",raiz->num);
pre_ordem(raiz->esq);
pre_ordem(raiz->dir);
}
}




/******************************************************/
IMPRIME EM ORDEM
/******************************************************/
void in_ordem(noh *raiz){
if(raiz!=NULL){
in_ordem(raiz->esq);
printf("%d \n",raiz->num);
in_ordem(raiz->dir);
}
}
/*****************************************************/
BALANCEAR
/*****************************************************/

noh *bl(noh *raiz){
if(raiz->fb == -2){
if(raiz->esq->fb == 1)
raiz->esq=rot_dir(raiz->esq);
raiz=rot_esq(raiz);
}else
if(raiz->fb == 2){
if(raiz->dir->fb == -1)
raiz->dir=rot_esq(raiz->dir);
raiz=rot_esq(raiz);
}
return (raiz);
}
/******************************************************/
INSERIR
/******************************************************/

noh *insere(noh *raiz,int num){
noh *novo=NULL;

if(!raiz){
novo=(noh *)malloc(sizeof(noh));
if(!novo){
printf("ERRO de alocao de memoria\n");
getch();
exit(1);
}
novo->esq=NULL;
novo->dir=NULL;
novo->num=num;
novo->fb=0;
printf("Insercao Concluida!\n");
return novo;
}else{
if(raiz->num==num){
printf("Numero ja consta, nao %c necessario
inseri-lo novamente!\n\n",130);
return(raiz);
}
if(num < raiz->num ){
raiz->esq=insere(raiz->esq,num);
}else{
raiz->dir=insere(raiz->dir,num);
}
raiz->fb=fb(raiz);
raiz=bl(raiz);
return raiz;
}
}
/******************************************************/
MAIN
/******************************************************/

void main(){
noh *avl=NULL;
int num,op;
do{
printf("\n\t\t Menu \n");
printf("\t\t 1 - Inserir \n");
printf("\t\t 2 - Imprimir em Ordem \n");
printf("\t\t 3 - Imprimir em Pre-Ordem \n");
printf("\t\t 4 - Imprimir em Pos-Ordem \n");
printf("\t\t 5 - Excluir \n");
printf("\t\t 6 - Altura \n");
printf("\t\t 7 - Destroi \n");
printf("\t\t 8 - Sair \n");
printf("\nOpcao: ");
scanf("%d",&op);
switch(op){
case 1: printf("Numero: ");
scanf("%d",&num);
avl=insere(avl,num);
break;

case 2: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Ordem:\n");
in_ordem(avl);
break;

case 3: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Pre - Ordem:\n");
pre_ordem(avl);
break;

case 4: if(!avl){
printf("\tArvore Vazia\n");
break;
}
printf("Arvore em Pos - Ordem:\n");
pos_ordem(avl);
break;

case 5: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
printf("Numero: ");
scanf("%d",&num);
avl=excluir(avl,num);
printf("Exclusao Sucedida!\n");
break;

case 6: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
num=alt(avl);
printf("Altura da Arvore: %d\n",num);
break;


case 7: if(!avl){
printf("\tArvore Vazia!\n");
break;
}
avl=destroi_avl(avl);
printf("Exclusao Concluida\n");
break;

case 8: exit(0);

default: printf("Opo Invalida!\n");
}
getch();
system("cls");
}while(op!=8);
}




Apostila de arvore AVL em linguagem C.
Autor: Kevim Brasil Maciel
Graduando de Cincia da Computao
Manaus/AM 2013

Você também pode gostar