Anda di halaman 1dari 5

rvores binrias de busca

Assim como rvores binrias generalizam a ideia de listas encadeadas, rvores binrias
de busca (= search trees) generalizam a ideia de listas encadeadas crescentes.

Definio
Considere uma rvore binria cujos ns tm um campo chave de um tipo linearmente
ordenado, ou seja, de um tipo (como int e string) que admite comparaes. Podemos
supor que os ns da rvore tm a seguinte estrutura:
struct cel {
int
chave;
int
conteudo;
struct cel *esq;
struct cel *dir;
};
typedef struct cel noh; /* n */

Uma rvore binria deste tipo de busca (em relao ao campo chave) se cada n X
tem a seguinte propriedade: a chave de X
1. maior ou igual chave de qualquer n na subrvore esquerda de X e
2. menor ou igual chave de qualquer n na subrvore direita de X.
Em outras palavras, se x um n qualquer ento
y->chave

x->chave z->chave

para todo n y na subrvore esquerda de x e todo n z na subrvore direita de x.


H uma outra maneira, talvez mais clara, de descrever esta propriedade: a ordem e-r-d
das chaves crescente.
Vamos examinar abaixo os problemas de busca, remoo e insero em rvores de
busca. Convm introduzir o tipo-de-dados arvore:
typedef noh *arvore; /* rvore */

Exerccios 1
1. Importante! Escreva uma funo que decida se uma dada rvore binria ou
no de busca.
2. Suponha que x->esq->chave x->chave x->dir->chave para cada n x
de uma rvore binria. Essa rvore de busca?

Busca

Considere o seguinte problema: Dada uma rvore de busca, encontrar um n da rvore


cuja chave tenha um certo valor k.
Eis uma funo recursiva que devolve um n x tal que x->chave == k; se um tal x no
existe, a funo devolve NULL:
// Recebe um inteiro k e uma rvore de busca r.
// Devolve um n cuja chave k;
// se tal n no existe, devolve NULL.
noh *busca( arvore r, int k) {
if (r == NULL || r->chave == k)
return r;
if (r->chave > k)
return busca( r->esq, k);
else
return busca( r->dir, k);
}

Eis uma verso iterativa da mesma funo:


while (r != NULL && r->chave != k) {
if (r->chave > k)
r = r->esq;
else
r = r->dir;
}
return r;

No pior caso, a busca consome tempo proporcional altura da rvore. Se a rvore for
balanceada. o consumo ser proporcional a log n, sendo n o nmero de ns. Compare
isso com a busca binria num vetor ordenado.

Exerccios 2
1. Escreva uma funo min que encontre uma chave mnima em uma rvore de
busca. Escreva uma funo max que encontre uma chave mxima.
2. Suponha que as chaves de nossa rvore de busca so distintas duas a duas.
Escreva uma funo que receba uma chave k e devolva a chave seguinte na
ordem crescente.
3. Escreva uma funo que transforme um vetor crescente em uma rvore de busca
balanceada.
4. Escreva uma funo que transforme uma rvore de busca em um vetor crescente.
Comece por alocar dinamicamente o vetor.
5. H uma relao muito ntima entre rvores de busca e o algoritmo de busca
binria num vetor. Qual , exatamente, essa relao?

Insero
Considere o seguinte problema: Inserir um novo n em uma rvore de busca. claro
que a rvore resultante deve tambm ser de busca; a que est a dificuldade do
problema.

Vamos adotar a poltica de criar o novo n fora da funo de insero:


noh *novo;
novo = mallocc( sizeof (noh));
novo->chave = k;
novo->esq = novo->dir = NULL;

A funo abaixo insere o novo n na rvore de busca r. O novo n ser uma folha da
rvore e o endereo da nova rvore ser igual a r a menos que r seja NULL.
//
//
//
//
//

A funo recebe uma rvore de busca r e uma


folha nova que no pertence rvore.
A funo insere o n novo na rvore
de modo que a rvore continue sendo de busca
e devolve o endereo da nova rvore.

arvore insere( arvore r, noh *novo) {


noh *f, *p;
if (r == NULL) return novo;
f = r;
while (f != NULL) {
p = f;
if (f->chave > novo->chave) f = f->esq;
else f = f->dir;
}
if (p->chave > novo->chave) p->esq = novo;
else p->dir = novo;
return r;
}

O cdigo da funo insere um tanto feio e deselegante. (Trata em separado do caso r


== NULL e a comparao das chaves na ltima iterao do while repetida depois do
while.) Para melhorar isso, podemos usar um ponteiro-para-ponteiro:
//
//
//
//
//

Recebe o endereo eer do endereo de uma


rvore de busca e uma folha avulsa novo.
Insere novo noh lugar correto da rvore e
atualiza *eer de modo que esse seja
o endereo da nova rvore.

void insere( arvore *eer, noh *novo) {


noh *p, *r;
r = *eer;
p = NULL;
while (*eer != NULL) {
p = *eer;
if (p->chave > novo->chave) eer = &(p->esq);
else eer = &(p->dir);
}
*eer = novo;
if (p != NULL) *eer = r;
}

Exerccios 3
1. Escreva uma verso recursiva de insere.

Remoo
Problema: Remover um n de uma rvore de busca de tal forma que a rvore continue
sendo de busca.
Comecemos tratando do caso em que o n a ser removido a raiz da rvore. Se a raiz
no tem um dos filhos, basta que o outro filho assuma o papel de raiz. Seno, faa com
que o n anterior raiz na ordem e-r-d assuma o papel de raiz.

Remoo de r. O n anterior a r na ordem e-r-d q (os ns esto numerados em ordem


e-r-d). O n q colocado no lugar de r, os filhos de r passam a ser filhos de q, e f
passa a ser filho (direito) de p.
//
//
//
//

Recebe uma rvore no vazia r. Remove a raiz


da rvore e rearranja a rvore de modo que
ela continue sendo de busca. Devolve
o endereo da nova raiz.

arvore removeraiz( arvore r) {


noh *p, *q;
if (r->esq == NULL) {
q = r->dir;
free( r);
return q;
}
p = r; q = r->esq;
while (q->dir != NULL) {
p = q; q = q->dir;
}
// q o n anterior a r na ordem e-r-d
// p o pai de q
if (p != r) {
p->dir = q->esq;
q->esq = r->esq;
}
q->dir = r->dir;
free( r);
return q;
}

Suponha agora que queremos remover um n que no a raiz da rvore. Para remover o
filho esquerdo de um n x faa
x->esq = removeraiz( x->esq);

e para remover o filho direito de x faa


x->dir = removeraiz( x->dir);

Exerccios 4
1. Suponha que as chaves 50, 30, 70, 20, 40, 60, 80, 15, 25, 35, 45, 36 so
inseridas, nesta ordem, numa rvore de busca inicialmente vazia. Desenhe a
rvore que resulta. Em seguida remova o n que contm 30.
2. Escreva uma verso recursiva da funo que remove um n de uma rvore de
busca.

O desempenho dos algoritmos


Quanto tempo gastam os algoritmos de busca, insero e remoo discutidos acima?
claro que esse tempo limitado pelo nmero de ns, digamos n, da rvore (pois nenhum
n visitada mais que 3 vezes). Mas esta uma resposta muito grosseira. Eis uma
resposta mais fina: qualquer dos algoritmos acima
gasta uma quantidade de tempo que, no pior caso, proporcional altura da rvore.
Concluso: interessa trabalhar sempre com rvores balanceadas, ou seja, rvores que
tm altura prxima a log n, isto , rvores em que todas as folhas tm aproximadamente
a mesma profundidade.
Infelizmente no fcil fazer isso. A altura da rvore sujeita a chuvas e trovoadas.
muito fcil construir um exemplo em que uma rvore comea com altura prxima de log
n mas depois de algumas inseres azaradas fica com altura muito mais prxima de n-1
(claro que o valor de n muda ao longo dessa brincadeira).
Os algoritmos de insero e remoo descritos acima no produzem rvores
balanceadas. Se a funo insere for repetidamente aplicada a uma rvore balanceada, o
resultado pode ser uma rvore bastante desbalanceada. Algo anlogo pode acontecer
depois de uma sequncia de chamadas da funo remove.
Para enfrentar essa situao preciso inventar algoritmos bem mais sofisticados e
complexos de insero e remoo; esses algoritmos fazem um rebalanceamento da
rvore aps cada insero e cada remoo. Vou apenas citar dois termos tcnicos
pitorescos: h um pacote de algoritmos de insero e remoo que produzem rvores
AVL; h um outro pacote que produz rvores rubro-negras.

Anda mungkin juga menyukai