rvores
16
12
20
15
12
18
22
14
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.
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).
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.
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:
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.
Estruturas de Dados
rvores
Expresso aritmtica:
Hierarquia:
(a + b) / c
/
A
+
F
Y
B
P
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
/
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.
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.