Вы находитесь на странице: 1из 7

Estruturas de Dados

rvores

Definio e terminologia bsicas


rvores (em ingls: trees) so estruturas de dados fundamentais na Cincia da Computao.
Permitem representar uma grande variedade de situaes e viabilizam a construo de algoritmos
eficazes para a resoluo de uma srie de importantes problemas computacionais. A figura 1
apresenta uma rvore simples que ser utilizada para ilustrar os principais conceitos e a
terminologia bsica envolvidos na criao e manipulao dessas estruturas de dados.
8

16

12

20

15

12

18

22

14

Fig. 1: Uma rvore binria ordenada.

Cada elemento de uma rvore chamado de n (em ingls: node). A rvore da figura 1
possui um total de 13 ns.

A ligao entre dois ns da rvore chamada de ramo (em ingls: branch). Como toda
rvore um grafo conexo sem ciclos, sabemos de antemo que a quantidade de ramos de
uma rvore igual sua quantidade de ns menos 1. Verifique que a rvore da figura 1
possui exatamente 12 ramos.

H uma hierarquia entre os elementos da rvore, de forma que para atingir um


determinado n, necessrio passar antes por uma quantidade 0 de outros ns. Os ns
que precisamos visitar para chegar a um n x so denominados ancestrais (em ingls:
ancestors) de x. Em contrapartida, dizemos que o n x um descendente (em ingls:
descendant) de cada um desses ancestrais. Dentre os ancestrais de um n x dado, aquele
n y que mais prximo de x chamado de pai de x. Na rvore da figura 1 o n contendo
um 16 o pai dos ns contendo os nmeros 12 e 20. Os descendentes diretos de um n
so seus filhos. Na rvore da figura 1 o n contendo um 16 tem como filhos os ns cujos
valores so 12 e 20. Frequentemente chamamos os ns que possuem o mesmo pai de
irmos.

O grau de uma rvore definido pela quantidade mxima de descendentes diretos que
um n pode ter. Na rvore da figura 1 um n pode ter no mximo 2 descendentes diretos,
ento dizemos que ela possui grau 2 ou, ento, que uma rvore binria (em ingls:
binary tree).

Dizemos que o n que possui grau 0, ou seja, que no possui descendentes, uma folha
(em ingls: leaf) da rvore. Na rvore do exemplo temos 6 folhas (os ns com os valores
2, 6, 12, 14, 18 e 22). Um n que possui pelo menos um descendente costuma ser
chamado de n interno (em ingls: internal node) da rvore, e a rvore da figura 1 possui
7 ns internos (os ns com os valores 8, 5, 16, 12, 20, 9 e 15).

Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
O conjunto de descendentes de um n, juntamente com suas ligaes ou ramos, uma
subrvore (em ingls: subtree). Uma rvore binria possui duas subrvores possveis
para cada n: a subrvore da esquerda e a subrvore da direita. A ttulo de exemplo, na
rvore da figura 1, em relao ao n com o valor 8, temos em sua subrvore da esquerda
os ns com os valores 5, 2 e 6, e na sua subrvore da direita, os ns com os valores 16,
12, 20, 9, 15, 18, 22, 12 e 14.

Um n que no possui ancestrais a raiz (em ingls: root) da rvore ou subrvore. Na


rvore do exemplo, a raiz da rvore o n com o valor 8. O n com o valor 5 a raiz da
subrvore composta pelos ns contendo os valores 5, 2 e 6.

A quantidade de ramos entre a raiz da rvore e a folha mais distante a altura (em
ingls: height) da rvore. A rvore da figura 1 possui altura = 4. O nvel (em ingls: level)
de um n a quantidade de ramos que existem entre ele e a raiz. Assim, a raiz possui
nvel 0, seus filhos possuem nvel 1, e assim sucessivamente. A definio utilizada aqui
para o conceito de nvel a mais comum, mas existem variaes importantes na definio
desse conceito.

Quando uma rvore possui suas subrvores com aproximadamente a mesma altura,
dizemos que ela balanceada. Uma rvore binria balanceada com n ns possui uma
altura aproximada de log2n. Um critrio muito comum utilizado para determinar se o
balanceamento ocorre ou no o critrio AVL (sigla devida s iniciais de seus criadores,
chamados Adelson, Velsky e Landis) onde a rvore considerada balanceada se para um
n x qualquer a altura de suas subrvores difere em no mximo 1.

Finalmente, com base nos conceitos e termos apresentados, podemos definir rvores de uma
maneira mais formal: uma rvore um conjunto finito de n elementos denominados ns de
forma que:

existe um elemento que a raiz da rvore;

os n - 1 elementos restantes formam, por sua vez, n 1 subrvores, das quais cada um
deles a raiz.

A definio de uma rvore , ento, recursiva, e comum utilizarmos algoritmos recursivos para
processar tais estruturas, pois uma lgica recursiva sobre uma estrutura de dados tambm
recursiva produz solues mais elegantes e fceis de manter.

Aplicaes
rvores so adequadas para representar hierarquias, como por exemplo, a ordem de execuo
das operaes requeridas por uma expresso aritmtica, ou a cadeia de subordinao de
conceitos (um organograma ou auto-relacionamento), ou ainda para representar eficientemente
valores que precisam ser pesquisados com freqncia. A figura 2 mostra dois exemplos clssicos
do uso de rvores: a representao de expresses aritmticas e uma hierarquia. Para ambos
mostrado o conceito original e a rvore correspondente.

Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
Expresso aritmtica:

Hierarquia:

(a + b) / c

/
A
+

F
Y

B
P

Fig. 2: Uso de rvores para modelar situaes concretas.

Representaes de rvores por encadeamento


A implementao de rvores por encadeamento muito flexvel e simples. Definimos um tipo de
dado para o n contendo a informao til a ser armazenada e membros para os ponteiros para os
seus descendentes diretos. Se a rvore for de grau 2, sero dois ponteiros, se for de grau 3, 3
ponteiros, de grau n, n ponteiros. Vamos construir uma rvore binria ordenada em que cada n
conter um valor inteiro positivo e a estrutura de dados do n poderia ser conforme mostrado na
figura 3. Nosso programa no se preocupar em manter a rvore balanceada, limitando-se a
acrescentar os novos elementos nas extremidades da rvore, como folhas, seguindo o critrio de
alocao em que na subrvore da esquerda de um n estaro os elementos cujo valor menor ou
igual ao seu e na subrvore da direita estaro alocados os elementos cujo valor maior que o
valor contido nesse n raiz.
struct regNo
{
struct regNo *esq;
int valor;
struct regNo *dir;
};
typedef struct regNo TNo;
Fig. 3: Definio do tipo de dados dos elementos da lista.

O programa precisar manter uma varivel descritora para indicar o elemento raiz da rvore, que
inicialmente ter um endereo vazio (NULL em C) e que, aps a criao do primeiro elemento,
passar a apontar para ele. A figura 4 ilustra a situao da rvore quando tiverem sido
informados pelo usurio os valores 8, 6, 16 e 2 e uma viso geral do cdigo fonte apresentada
na listagem a seguir.
Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
raiz

esq

esq

esq
/

valor
2

valor
5

dir
/

valor
8

dir

dir

esq
/

esq
/

valor
6

valor
16

dir
/

dir
/

Fig. 4: Situao parcial da construo da rvore.


#include <stdio.h>
struct regNo
{ struct regNo *esq;
int valor;
struct regNo *dir;
};
typedef struct regNo TNo;
TNo *AchaPai( TNo *r, int n );
void ImprimeArvore(TNo *r, int n);
int ContaNos(TNo *r);
int SomaNos(TNo *r);
int ContaPares(TNo *r);
int main()
{ TNo *raiz = NULL, *aux, *pai;
int numero;
while(1)
{
printf("\nInforme o valor:\n"); scanf("%d", &numero);
if( numero < 0 ) break;
aux = (TNo *) malloc( sizeof(TNo) );
aux->valor = numero;
aux->dir = NULL;
aux->esq = NULL;
/* Fazendo o encadeamento do novo noh */
pai = AchaPai( raiz, numero );
if( pai == NULL )
raiz = aux;
else
if( numero <= pai->valor )
pai->esq = aux;
else
pai->dir = aux;
}
printf("\n\nA arvore possui %d elementos:\n", ContaNos(raiz));
ImprimeArvore(raiz, 0);
Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
return 0;
}

Para determinar o ancestral do novo n o programa utiliza a rotina AchaPai, que recebe como
parmetros o endereo de um n da rvore e o valor contido no novo n e retorna o endereo do
ancestral. Caso o endereo correspondente ao primeiro parmetro seja nulo, a rotina retorna nulo.
A implementao recursiva da rotina AchaPai mostrada na figura 5, e uma verso no
recursiva equivalente apresentada na figura 6.
TNo *AchaPai( TNo *r, int n )
{
if( r == NULL )
return NULL;
else
if( n <= r->valor )
/* n descendente do lado esquerdo de r */
if( r->esq == NULL )
return r;
else
return AchaPai( r->esq, n );
else
/* n descendente do lado direito de r */
if( r->dir == NULL )
return r;
else
return AchaPai( r->dir, n );
}
Fig. 5: Implementao recursiva da rotina AchaPai.
TNo *AchaPai( TNo *r, int n )
{
TNo *c;
if( r == NULL )
return NULL;
else
{
c = r;
while(1)
{
if( n <= c->valor )
/* n descendente do lado esquerdo de c */
if( c->esq == NULL )
return c;
else
c = c->esq;
else
/* n descendente do lado direito de c */
if( c->dir == NULL )
return c;
else
c = c->dir;
}
}
}
Fig. 6: Implementao no-recursiva da rotina AchaPai.

Aps a incluso dos elementos na rvore, o programa imprime quantos ns existem na rvore e,
em seguida, os valores nela contidos. Para determinar quantos elementos existem na rvore
utilizada a funo ContaNos, que recebe como parmetro o endereo de um n da rvore e
retorna um inteiro com o valor da contagem, como mostra o cdigo apresentado na figura 7. A
abordagem adotada nessa rotina , recursivamente, somar 1 referente ao n raiz da rvore mais a
Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
quantidade de ns para a subrvore da sua esquerda mais a quantidade de ns para a subrvore
da sua direita.
int ContaNos(TNo *r)
{
if(r == NULL)
return 0;
else
return 1 + ContaNos(r->esq) + ContaNos(r->dir);
}
Fig. 7: Implementao recursiva da rotina ContaNos.

Para imprimir o contedo da rvore o programa empregada a rotina ImprimeArvore, que


recebe como parmetros o endereo de um n da rvore e um inteiro correspondente ao seu nvel
e faz a impresso. A rotina alinha a raiz geral da rvore esquerda da tela e, medida que o
nvel dos ns vai aumentando, os seus valores vo sendo exibidos progressivamente mais
direita na tela, como ilustrado na figura 8. A figura 9 mostra uma implementao recursiva dessa
rotina e na figura 10 apresentada uma verso no-recursiva, a ttulo de exemplo. Nessa verso
no-recursiva necessria a utilizao de uma pilha, para manter as referncias ao ancestral de
um n. As duas rotinas de impresso primeiro processam o elemento da raiz e, em seguida,
tratam as suas subrvores, primeiro a esquerda, depois a direita. Essa abordagem raiz-subrvoresubrvore denominada Pr-Ordem (em ingls: pre-order). Caso primeiro fizssemos a
impresso de uma subrvore, depois da raiz e ento da subrvore restante, teramos a abordagem
Em-Ordem (em ingls: in-order). A terceira possibilidade, que processa os dados na sequncia
subrvore-subrvore-raiz, chamada de Ps-Ordem (em ingls: pos-order). Para rotinas como a
de impresso, adotar um desses critrios tem grande influncia no resultado da rotina, mas em
outras, como a de contagem dos ns, o critrio adotado irrelevante.
8
5
2
6
16
12
9
12
15
14
20
18
22
Fig. 8: Resultado impresso pela rotina ImprimeArvore para a rvore da figura 1.
void ImprimeArvore(TNo *r, int n)
{
int c;
if( r != NULL )
{
for( c=0; c<n; c++) printf("\t");
printf("%d\n", r->valor);
ImprimeArvore(r->esq, n+1);
ImprimeArvore(r->dir, n+1);
}
}
Fig. 9: Implementao recursiva da rotina ImprimeArvore.

Prof. Antonio Cesar de Barros Munari

Estruturas de Dados

rvores
void ImprimeArvore(TNo*r, int n)
{
typedef struct{ TNo *ender; int nivel;} TPilha;
TPilha pilha[1000];
TNo *noh;
int d, topo = 0;
noh = r;
while(noh != NULL || topo > 0)
{
if(noh != NULL)
{
for( d=0; d<n; d++) printf("
printf("%d\n", noh->valor);

");

pilha[topo].ender = noh;
pilha[topo].nivel = n;
topo++;
noh = noh->esq;
n++;
}
else
{

topo--;
noh = pilha[topo].ender;
n
= pilha[topo].nivel;
noh = noh->dir;
n++;

}
}
}
Fig. 10: Implementao no-recursiva da rotina ImprimeArvore.

Prof. Antonio Cesar de Barros Munari