Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web


---------------------------
ÁRVORES BINÁRIAS - PARTE #2
---------------------------

        Para a manipulação de árvores binárias, é necessário a  implementação
das seguintes rotinas:

        * VisitaArvore ();
        * AdicionaNo ();
        * NoSucessor ();
        * NoPai ();
        * ExcluiNo ();

	1)   VisitaArvore:  esta  é  uma  rotina  recursiva  que  pode  ser
"in-order", "pos-order" ou "pre-order". Esses termos dizem respeito à ordem
com que os nós são visitados:
	VisitaPreOrder (No: Raiz)
	.	se Raiz = NULL então retorna
	.	ProcessaNo (Raiz)
	.	VisitaPreOrder (Raiz.LinkEsquerdo)
	.	VisitaPreOrder (Raiz.LinkDireito)
	fim

	VisitaPosOrder (No: Raiz)
	.	se Raiz = NULL então retorna
	.	VisitaPosOrder (Raiz.LinkEsquerdo)
	.	VisitaPosOrder (Raiz.LinkDireito)
	.	ProcessaNo (Raiz)
	fim

	VisitaInOrder (No: Raiz)
	.	se Raiz = NULL então retorna
	.	VisitaInOrder (Raiz.LinkEsquerdo)
	.	ProcessaNo (Raiz)
	.	VisitaInOrder (Raiz.LinkDireito)
	fim

        A rotina "VisitaInOrder" é a mais importante pois essa visita os  nós
em ordem crescente independentemente de sua  disposição na  árvore. A  rotina
"VisitaPosOrder"  também é muito importante,  pois  visita  os  nós  do  mais
distante ao mais próximo da raiz, o que é utilizado para desalocar a  memória
utilizada pelos nós. A função "ProcessaNo" é uma função abstrata que pode ser
qualquer coisa que seja necessária, como por  exemplo,  mostrar a  informação
guardada no nó.


        2) AdicionarNó: Esta função deve  encontrar um nó que possa  "adotar"
o nó a ser guardado na árvore e guardá-lo:
	AdicionarNó (Dado: X; No: Raiz)
	.	Nó: Pai, Filho
	.
	.	(procura do nó que vai receber X como filho)
	.	Filho = Raiz
	.	Enquanto Filho <> NULL faça
	.	.	Pai = Filho
	.	.	se X <= Pai.Dado então
	.	.		Filho = Pai.LinkEsquerdo
	.	.	senão
	.	.		Filho = Pai.LinkDireito
	.	Fim_Enquanto
	.
	.	(Nó é requisitado e adotado por PAI)
	.	Filho = RequisitaNo ()
	.	se X <= Pai.Dado então
	.		Pai.LinkEsquerdo = Filho
	.	senão
	.		Pai.LinkDireito = Filho
	fim
        3) NóSucessor:  Esta  rotina  procura o sucessor, ou seja, o menor nó
de informação maior a de um  determinado  nó.  Esse  nó,  devido à forma  que
é construída a árvore, é o nó mais à esquerda dos nós que estão à direita  do
nó a que estamos nos referindo. Confuso? Pense um pouco sobre isso analisando
a figura e depois construa você mesmo uma árvore em um papel.
	(retorna o sucessor de X ou X, caso X não tenha sucessor)
	Nó: NóSucessor (Nó: X)
	.	Nó: Pai, Filho
	.
	.	Pai = X
	.	X = Pai.LinkDireito
	.	Enquanto Filho <> NULL faça
	.	.	Filho = Pai.LinkEsquerdo
	.	Fim_Enquanto
	.	retorna Pai
	fim
        4) NóPai: Esta rotina procura o pai de um determinado nó:
	(procura o pai de X a partir de uma determinada Raiz)
	Nó: NóPai (Nó: X, Raíz)
	.	se Raíz = NULL então retorna NULL
	.	se (Raiz.LinkEsquerdo = X) ou (Raiz.LinkDireito = X)
	.		então retorna Raiz
	.	se Raíz.Dado <= X.Dado então
	.		retorna NóPai (X, Raiz.LinkEsquerdo)
	.	senão
	.		retorna NóPai (X, Raiz.LinkDireito)
	fim		
        ExcluirNó: Esta, que é a rotina mais coplexa de todas as demonstradas
nesta  seção,  exclui,  mas  não  destroi  um  determinado  nó.  Ela  retorna
o seu  endereço, para que a rotina que a evocou possa excluir ou  mover  esse
nó. Existem 3 possibilidades:

        * X ser um nó folha. Aí a exclusão é muito  simples: basta fazer  com
que seu pai não faça referência a ele mais.
        * X ter apenas um filho. Nesse caso, o seu pai "adota" seu filho.
        * X tem dois filhos. Aí o seu sucessor deve ser excluído  da  árvore,
ser adotado pelo pai de X e adotar os filhos de X.
	Nó: ExcluiNó (Nó: X, Raiz)
	.	Nó: Pai, Filho, Sucessor
	.
	.	Pai = NóPai (X, Raiz)
	.
	.	(se X for um nó-folha...)
	.	se (X.LinkEsquerdo = NULL) e (X.LinkDireito = NULL) então
	.	.	se Pai.LinkExquerdo = X então
	.	.		Pai.LinkEsquerdo = NULL
	.	.	senão
	.	.		Pai.LinkDireito = NULL
	.	.	retorna X
	.	fim_se
	.
	.	(se X tiver apenas um filho...)
	.	se (X.LinkEsquerdo = NULL) ou (X.LinkDireito = NULL) então
	.	.
	.	.	(descobre quem é o único filho de X)
	.	.	se X.LinkEsquerdo = NULL então
	.	.		Filho = X.LinkDireito		
	.	.	senão
	.	.		Filho = X.LinkEsquerdo
	.	.
	.	.	(o Pai de X adota seu filho)
	.	.	se Pai.LinkExquerdo = X então
	.	.		Pai.LinkEsquerdo = Filho
	.	.	senão
	.	.		Pai.LinkDireito = Filho
	.	.
	.	.	retorna X
	.	fim_se
	.
	.	(se X tiver dois filhos...)
	.	Sucessor = NóSucessor (X)
	.	ExcluirNó (Sucessor, Raiz)
	.
	.	(o pai de X adota o Sucessor)
	.	se Pai.LinkExquerdo = X então
	.		Pai.LinkEsquerdo = Sucessor
	.	senão
	.		Pai.LinkDireito = Sucessor
	.
	.	(o Sucessor adota os filho de X)
	.	Sucessor.LinkEsquerdo = X.LinkEsquerdo
	.	Sucessor.LinkDireito = X.LinkDireito
	.
	.	retorna X
	fim

        Note que este algoritmo não verifica condições de erro e nem se o  nó
a ser excluído é a raiz. Note também que esta última rotina  evoca a si mesma
para excluir o nó Sucessor, por isso a tarefa de  desalocar o  nó excluído  é
atribuída à rotina que a chamou, um nível acima.


        Este  tipo  de estrutura, em  condições  ótimas, é muito  eficiente e
muito rápido, se  comparado à  estruturas  lineares. A  profundidade  de  uma
árvore binária é dada por P = log2 (T+1)-1, ou seja, se uma árvore tiver, por
exemplo, 63 nós, qualquer nó procurado estará no máximo a 5 nós da raiz.  Mas
para que isso aconteça, é necessário que a árvore esteja balanceada, ou seja,
é necessário que o número de nós a direita e a esquerda  de  cada nó  seja  o
mesmo ou perto disso. Não é preciso muito esforço mental  para  perceber  que
uma árvore binária desbalanceada pode se tornar uma lista encadeada  simples.
O que seria desastroso em termos de performance.


        Para saber mais...

------> Exemplo de Árvores Binnárias em Pascal
------> Árvores Binárias - parte #1
------> Pilhas
------> Recursividade

[HOME]