• Nenhum resultado encontrado

Árvores

N/A
N/A
Protected

Academic year: 2021

Share "Árvores"

Copied!
18
0
0

Texto

(1)

Em Construção, com vi!!! Em Construção, com vi!!! Data da última atualização: 13/03/2000 Data da última atualização: 13/03/2000 copyright Adriano Cruz 1999

copyright Adriano Cruz 1999

Árvores

Árvores

Introdução Introdução 1. 1. Definições Básicas Definições Básicas 2. 2. Àrvores Binárias Àrvores Binárias 3. 3.

Armazenamento de Árvores Binárias Armazenamento de Árvores Binárias 4.

4. Um

Uma Aplicação de a Aplicação de Árvores BináriasÁrvores Binárias 5.

5.

Percorrendo Árvores Binárias Percorrendo Árvores Binárias 6. 6. O Algoritmo de Huffman O Algoritmo de Huffman 7. 7.

Removendo Nós de Árvores Binárias Removendo Nós de Árvores Binárias 8.

8.

Árvores Binárias Balanceadas

Árvores Binárias Balanceadas

9. 9. Exercícios Exercícios 10. 10.

Introdução

Introdução

Definições Básicas

Definições Básicas

Árvores são estruturas de

Árvores são estruturas de dados extremamente útdados extremamente úteis em muitas aplieis em muitas aplicações. Uma árvore é formada cações. Uma árvore é formada por um conjunto finitopor um conjunto finito T T dede elementos denominados vértices ou nós de tal modo que se

elementos denominados vértices ou nós de tal modo que se T = 0 a árvore T = 0 a árvore é vazia, caso contráé vazia, caso contrário temos um nó especial chamado raizrio temos um nó especial chamado raiz da árvore (

da árvore (r r ), e cujos elementos restantes são particionados em), e cujos elementos restantes são particionados em mm>=1 conjuntos distintos não vazios, as subárvores de>=1 conjuntos distintos não vazios, as subárvores de rr,,sendo cada umsendo cada um destes conjuntos por sua vez uma árvore.

destes conjuntos por sua vez uma árvore.

A forma convencional de representar uma árvore está indicado na figura

A forma convencional de representar uma árvore está indicado na figura ainiainiabaixo. Esta árvore tem nove nós sendoabaixo. Esta árvore tem nove nós sendo A Ao nó raiz.o nó raiz.

Figura (aini): Uma árvore Figura (aini): Uma árvore

(2)

Os conjuntos das subárvores tem de ser disjuntos tem de ser disjuntos portanto a estrutura indicada na Figura arvn não é uma árvore.

Figura arvn: Estrutura que não representa uma árvore

Sené um nó da árvore T entãoT nindica uma subárvore deT com raiz no nón. Os nósn1 , n2 , ..., nk das subárvores deT nsão chamados de filhos de nené o pai destes nós, que são nós irmãos. Os nós BeC são filhos de Ae nós irmãos. Nós sem filhos como os nós D, H, I,  F e Gsão chamados de folhas. A subárvore da esquerda do nó Atem raiz em Be a subárvore da direita tem raiz emC,isto está

indicado pelos dois ramos saindo de A. A ausência de um ramo na árvore indica uma subárvore vazia, como a subárvore da direita do nó B. O número de de filhos de um nó é chamado de grau de saída deste nó. Por exemplo, o nó C tem grau de saída 3 e o nó E grau 2. Se o nóné a raiz de uma subárvore T nen1pertence aT nentãon1é descendente de nenancestral den1. Portanto nós sem

descendentes próprios é uma folha. Por exemplo, o nó H é ancestral do nóC e o nó Dé descendente do nó A.

Um caminho da árvore é composto por uma seqüência de nós consecutivos (n1 , n2 , ..., nk-1 , nk ) tal que existe sempre a relação nié pai deni+1. Osk vértices formamk-1pares e um caminho de comprimento igual a k -1. O comprimento do caminho entre o nó Ae o nó H  é 3.

O nível de um nó n pode ser definido do seguinte modo: o nó raiz tem nível 0, os outros nós tem um nível que é maior uma unidade que o nível de seu pai. Na árvore da figura anterior temos nós nos seguintes níveis:

nível 0 = A nível 1 = B, C nível 2 = D, E, F, G nível 3 = H, I

A altura de um nó né o número de nós do maior caminho denaté um de seus descendentes. As folhas tem altura 1.

Existem diversas maneiras de representar árvores. Uma representação que reflete a idéia de árvores como conjuntos aninhados é mostrado na figura arvconj abaixo. A figura mostra o mesmo conjunto da figura aini.

(3)

Figura (arconj): Árvore representada como conjuntos aninhados.

Uma outra notação que encontramos a toda hora, e que está representada na figura arviden, é a forma identada ou de diagrama de  barras. Notar que esta representação lembra um sumário de livro. Os sumários dos livros são representações da árvore do conteúdo do

livro.

Figura (arviden) Árvore e sua representação por barras

Uma outra forma interessante de representar uma árvore é a representação por parênteses aninhados. Da mesma forma que a figura aini representa uma árvore no plano a representação por parênteses representa uma árvore em uma linha. A seqüência de parênteses

representa a relação entre os nós da estrutura. O rótulo do nó é inserido à esquerda do abre parênteses correspondente. A árvore representada planarmente pela figura aini pode ser representada em uma linha por 

(A (B(D))(C(E(H)(I))(F)(G)))

Esta representação tem importância, por exemplo, no tratamento de expressões aritméticas, já que toda expressão aritmética pode ser  colocada nesta forma. Se colocarmos uma expressão nesta forma podemos então representá-la como uma árvore, mostrando como ela seria calculada. Para colocarmos uma expressão em forma de árvore devemos considerar cada operador como um nó da árvore e os seus operandos como as duas subárvores. Considere a expressão C seguinte

 A + (B-C)*D%(E*F) que após receber todos os parênteses fica da seguinte maneira

(4)

A figura arvexp mostra como fica esta expressão representada por uma árvore.

Figura (arvexp) Uma expressão e sua representação como árvore.

Árvores Binárias

A figura arvbin abaixo mostra um importante tipo de árvore que é a árvore binária. Em uma árvore binária cada nó tem no máximo duas subárvores, e quando há somente uma presente é necessário distinguir entre subárvore esquerda e direita. Árvores binárias podem ser vistas em diversas situações do cotidiano. Por exemplo, um torneio de fut ebol eliminatório, do tipo das copas dos países, como a Copa do Brasil, em que a cada etapa os times são agrupados dois a dois e sempre são eliminados metade dos times é uma árvore  binária.

Figura abin: Árvore binária

Formalmente uma árvore binária pode ser definida como um conjunto finito de nós, que é vazio, ou consiste de um nó raiz e dois conjuntos disjuntos de nós, a subárvore esquerda e a subárvore direita. É importante observar que uma árvore binária não é um caso especial de árvore e sim um conceito completamente diferente. Por exemplo, considere a figura arvbind, note que são duas árvores idênticas, mas são duas árvores binárias diferentes. Isto porque uma delas tem a subárvore da direita vazia e a outra a subárvore da esquerda.

(5)

Figura arcbind: Árvores binárias diferentes.

Uma árvore estritamente binária é uma árvore binária em que cada nó tem 0 ou 2 filhos. Uma árvore binária cheia é uma árvore em que se um nó tem alguma sub-árvore vazia então ele está no último nível. Uma árvore completa é aquela em se né um nó com algumas de subárvores vazias, então nse localiza no penúltimo ou no último nível. Portanto, toda árvore cheia é completa e estritamente binária. A Figura arvbcc mostra uma árvore estritamente binária, completa e cheia.

Armazenamento de Árvores Binárias

Para armazenar cada nó de uma árvore binária precisamos de uma estrutura que contenha dois ponteiros: um aponta para a subárvore esquerda e outro para a subárvore direita. Naturalmente, devemos ter o(s) campo(s) para armazenar as informações que o nó deve conter. Nos algoritmos que iremos mostrar consideraremos que existe a seguinte definição para a e strutura do nó:

typedef struct sttNo { tipo inf;

struct sttNo *esq, *dir; } tNo;

A Figura armarv mostra um diagrama de como seria o armazenamento de uma árvore binária. Observar que se desconsiderarmos os campos de informação para armazenar uma árvore com nnós precisamos de 2n+1 unidades de memória.

(6)

 No processo de criar uma árvore precisaremos de três operações importantes: cria_arvore,pos_esqepos_dir.cria_arvore

cria uma árvore binária nova consistindo de um único nó, armazena a informação. e retorna um ponteiro para este nó. Um algoritmo  para esta função pode ser o seguinte:

p = cria_no(); p->info = x; p->esq = NULL; p->dir = NULL; return p;

pos_esqaceita um ponteiro p para uma árvore binária sem filho esquerdo e cria um novo filho esquerdo contendo a informação x.

Um possível algoritmo para esta função pode ser: if (p->left) puts("Operação ilegal"); else { q = cria_arvore(); p->left = q; }

O algoritmopos_diré semelhante a este com a diferença que ele cria um nó a direita.

Uma Aplicação de Árvores Binárias

As árvore binárias são estruturas importantes toda vez que uma decisão binária deve ser tomada em algum ponto de um algoritmo. Vamos agora, antes de passar a algoritmos mais complexos, mostrar uma aplicação simples de árvores binárias. Suponhamos que  precisamos descobrir números duplicados em uma lista não ordenada de números. Uma maneira é comparar cada novo número com

todos os números já lidos. Isto aumenta em muito a complexidade do algoritmo. Outra possibilidade é manter uma lista ordenada dos números e a cada número lido fazer uma busca na lista. Outra solução é usar uma árv ore binária para manter os números. O primeiro número lido é colocado na raiz da árvore. Cada novo número lido é comparado com o elemento raiz, caso seja igual é uma duplicata e voltamos a ler outro número. Se é menor repetimos o processo com a á rvore da direita e se maior com a árvore da esquerda. Este  processo continua até que uma duplicata é encontrada ou uma árvore vazia é achada. Neste caso, o número é inserido na posição

devida na árvore. Considere que os números

7 8 2 5 8 3 5 10 4

(7)

O programa arv0300.c mostra este algoritmo. O programa insere os nós na árvore e imprime uma mensagem caso seja fornecido um número que já foi lido antes.

/* programa arv0300.c */ #include<stdio.h>

#include<stdlib.h> #include<string.h> typedef struct stNo {

int info;

struct stNo *esq, *dir; } tNo ;

tNo *cria_arvore( int ); tNo *cria_no( );

void pos_esq (tNo *, int ); void pos_dir (tNo *, int ); void main() {

tNo *raiz, *p, *q;

char linha[80], *numero; int num;

gets(linha);

numero = strtok(linha, " "); /* pega o primeiro numero da lista */ num = atoi(numero);

raiz = cria_arvore(num); /* insere na r aiz */ numero = strtok(NULL, " ");

while (numero) { q = raiz; p = raiz;

printf("Li numero %d\n", num); /* le novo numero */ num = atoi(numero);

while (num != p->info && q) { /* procura na arvore */ p = q;

if (num < p->info)

q = p->esq; /* passa para arvore esquerda */ else

q = p->dir; /* passa para direita */ }

if (num == p->info)

printf("O numero %d ja existe na arvore.\n", num); else { /* vou inserir o numero na arvore */

if (num < p->info) pos_esq(p, num); else pos_dir(p, num); } numero = strtok(NULL, " "); } /* fim do while (numero) */

(8)

}

tNo *cria_arvore (int x) { tNo *p; p = cria_no (); if (p) { p->info = x; return p; } else {

puts("Faltou espaco para alocar no."); exit(1);

} }

tNo *cria_no() { tNo *p;

if ((p = (tNo *) malloc(sizeof(tNo))) = = NULL) return NULL;

else {

p->esq = NULL; p->dir = NULL; return p;

} }

void pos_esq(tNo *p, int x) { tNo *q;

if (p->esq)

puts("Operacao de insercao a esquerda ilegal."); else {

q = cria_arvore(x); p->esq = q;

} }

void pos_dir(tNo *p, int x) { tNo *q;

if (p->dir)

puts("Operacao de insercao a direita ilegal."); else {

q = cria_arvore(x); p->dir = q;

} }

Percorrendo Árvores Binárias

Uma operação muito comum é percorrer uma árvore binária, o que significa passar por todos os nós, pelo menos uma vez. O conceito de visitar significa executar uma operação com a informação armazenada no nó, por exemplo, imprimir seu conteúdo. Na operação de  percorrer a árvore pode-se passar por alguns nós mais de uma vez, sem porém visitá-los.

Uma árvore é uma estrutura não seqüêncial, diferentemente de uma lista, por exemplo. Não existe ordem natural para percorrer árvores e portanto podemos escolher diferentes maneiras de percorrê-las. Nós iremos estudar três métodos para percorrer árvores. Todos estes três métodos podem ser definidos recursivamente e se baseiam em três operações básicas: visitar a raiz, percorrer a subár vore da esquerda e percorrer a subárvore da direita. A única diferença entre estes métodos é a ordem em que estas operações são executadas. O primeiro método, conhecido como percurso em pré-ordem, implica em executar recursivamente os três passos na seguinte ordem.

Visitar a raiz; 1.

Percorrer a subárvore da esquerda em pré-ordem; 2.

Percorre a subárvore da direita em pré-ordem. 3.

(9)

G I. Uma aplicação interessante deste tipo de percurso é aplicá-lo à uma árvore que contenha uma expressão aritmética, a qual foi expandida e recebeu todos os parênteses. Por exemplo, aplicando-se o percurso em pré-ordem à árvore arvexp obtém-se como resultado a expressão em notação polonesa normal, isto é os operandos antes dos operadores. Deste modo o resultado é

+A*-BC%D*EF. Observar que esta notação é diferente da notação polonesa reversa, em que os operadores aparecem depois dos operandos.

Um algoritmo recursivo para implementar este modo de percurso pode ser o seguinte: void pre_ordem ( tipoNo *pt) {

if (pt) { visita (pt); pre_ordem (pt->esq); pre_ordem (pt->dir); } }

Para percorrer a árvore em ordem simétrica executa-se recursivamente os três passos na seguinte ordem: Percorrer a subárvore da esquerda em ordem simétrica;

1.

Visitar a raiz; 2.

Percorrer a subárvore da direita em ordem simétrica. 3.

Um algoritmo recursivo para implementar este modo de percurso pode ser o seguinte: void em_ordem ( tipoNo *pt) {

if (pt) { em_ordem (pt->esq); visita (pt); em_ordem (pt->dir); } }

Para a árvore da Figura arvbin o percurso forneceria o seguinte resultado A B C D E F G H I.

Este tipo de percurso é muito empregado em árvores binárias de busca. Considere a árvore mostrada na Figura arvbus, que foi gerada como está indicado na seção Uma Aplicação de Árvores Binárias. Caso a árvore seja percorrida em ordem simétrica o resultado seria

2 3 4 5 7 8 10 que são os números lidos em ordem crescente sem repetição.

O percurso conhecido como pós-ordem é feito a partir dos três passos na seguinte ordem:

Percorrer a subárvore da esquerda em pós-ordem; 1.

Percorrer a subárvore da direita em pós-ordem; 2.

Visitar a raiz; 3.

(10)

este percurso poderia ser: void pos_ordem(tipoNo *pt) { if (pt) { pos_ordem(pt->esq); pos_ordem(pt->dir); visita(pt); }

O percurso em pós-ordem pode ser aplicado no cálculo da altura de uma árvore. Para calcular a altura de uma árvore é necessário calcular o maior caminho da raiz até uma de suas folhas. Deste modo só podemos calcular o comprimento de um caminho a partir de um nóvapós percorrermos todos os seus descendentes. O algoritmo mostrado abaixo mostra como fica a implementação da função visitapara que ela calcule a altura do nó.

void visita (tNo *p) { int alt1, alt2;

if (p->esq) alt1 = p->esq->altura; else alt1 = 0;

if (p->dir) alt2 = p->dir->altura; else alt2 = 0;

if (alt1>alt2) p->altura = alt1 + 1; else p->altura = alt2 + 1;

printf("info = %d ", p->info); printf("altura = %d\n", p->altura); }

As variáveisalt1ealt2armazenam a altura das subárvores da esquerda e da direita e o campo alturaé um novo campo da estrutura que armazena o nó. A altura de um nó é igual ao maior valor entre as alturas esquerda e direita incrementado de um.

Algoritmo de Huffman

Para analisarmos mais uma aplicação de árvores binárias vamos considerar o problema de codificar uma mensagem composta de uma seqüência de símbolos de um alfabeto de nsímbolos. Esta mensagem será transformada em uma seqüência de bits, depois de a cada símbolo for atribuído um código binário e os códigos dos símbolos da mensagem forem concatenados.

Considere um alfabeto composto de quatro símbolos A, B, C e D, sendo que a cada um dos símbolos foi atribuído o código indicado a seguir: Símbolo Código A 00 B 01 C 10 D 11

A mensagem ABCADCA seria codificada da seguinte maneira 00011000111000, tendo comprimento de 14 bits. O objetivo do algoritmo é criar um código que minimize o comprimento da mensagem. Para criar este código vamos levar em conta a freqüência de cada símbolo na mensagem. A Tabela a seguir mostra a freqüência de cada símbolo na mensagem

Símbolo Freqüência

A 3

B 1

C 2

D 1

Desta tabela podemos verificar que se atribuirmos ao símbolo A um código binário mais curto que os at ribuídos aos símbolos B e D teríamos uma mensagem menor. Isto provém do fato que o símbolo A aparece mais vezes do que os símbolos B e D. Suponha que os

(11)

seguintes códigos sejam atribuídos aos símbolos Símbolo Código A 0 B 110 C 10 D 111

Usando estge código a mensagem ABCADCA ficaria 0110100111100 que requer 13 bits. Em mensagens longas com mais símbolos infrequentes o ganho pode ser maior. Um dos requerimentos deste código é que nenhum código seja prefixo de outro, c aso a

decodificação seja feita da esquerda para direita.

Para decodificar a mensagem vamos começar da esquerda para a direita, caso o primeiro bit seja 0 o código corresponde ao símbolo A.  No caso contrário devemos continuar a examinar os bits restantes. Se o segundo bit for 0 o símbolo é um C, caso contrário e xaminamos

o terceiro bit, um 0 indica um B e D no outro caso.

Do que vomos até agora o algoritmo para encontrar o algoritmo ótimo é o seguinte. Encontre os dois símbolos que aparecem com menor freqüência, no nosso caso B e D. Atribuímos 0 para B e 1 para D. Combine estes dois símbolos em um BD. Este novo símbolo terá freqüência igual a soma das freqüências de B e D, no caso 2. Temos agora os seguintes símbolos A (3), C (2) e BD (2), os números entre parênteses são as freqüências. Novamente devemos escolher os símbolos de menor freqüência, que são C e BD. Atribuímos o código 0 ao símbolo C e 1 ao BD. Isto siginifica adicionar 1 aos códigos de B e D, que passam a valer 10 e 11 respectivamente. Os dois símbolos são combinados então no símbolo CBD de freqüência 4. Temos agora dois símbolos A (3) e CBD (4). Atribuímos 0 ao símbolo A e 1 ao símbolo CBD. O símbolo ACBD é o único símbolo restante e recebe o código NULL de comprimento 0. A Figura arvhuf1 mostra a árvore binária que pode ser construída a partir deste exemplo. Cada nó representa um símbolo e sua freqüência.

Figura arvhuf1: Árvore de Huffman Vamos considerar outro exemplo em que temos

(12)

Removendo Nós de Árvores Binárias

Para remover um nó de uma árvore binária devemos considerar três casos: nó sem filhos;

1.

nó com um unico filho; 2.

nó com dois filhos. 3.

O caso de um nó sem filhos é o mais simples e significa apenas ajustar o ponteiro de seu pai. A Figura remov0 ilustra este caso, onde o nó com o valor 8 é removido. No caso do nó ter um único filho a mudança na árvore também é simples significa mover o nó filho daquele será removido uma posição para cima como está ilustrado na Figura remove1, onde o nó com o valor 6 é removido. O caso mais complexo é o do nó com dois filhos. Neste caso devemos procurar o sucessor  s(ou antecessor) do nó deverá ocupar este lugar. Este nó (sucessor) é um descendente que está na subárvore da direita do nó e corresponde ao nó mais à esquerda desta árvore. Ele não tem filhos à esquerda e a sua árvore à direita pode ser movida para o lugar de s. A Figura remove2 ilustra o caso de remoção do nó com o valor 12. Observe que o nó 13 (sucessor) assumiu o lugar do nó 12.

(13)

Figura remove0: Removendo nó (8) sem filhos.

(14)

Figura remov2: Removendo nó (12) com dois filhos.

O texto abaixo mosta uma rotina que remove nós de uma árvore, que contém números inteiros. O programa completo está em arvremov.c.

tNo *remover (tNo *tree, int num) {

tNo *p, /* p aponta para o no a ser removido */ *q, /* q aponta para o pai do no */

*rp, /* rp aponta que ira substituir o no p */ *f,

*s; /* sucessor do no p */ p = tree; q=NULL;

/* procura o no com a chave num, p aponta para o no e q aponta para o pai do no */

while ( p && p->info != num) { q = p; if ( num < p->info) p = p->esq; else p = p->dir; } /* fim do while */

if (!p) return NULL; /* a chave nao existe na arvore */

/* agora iremos ver os dois p rimeiros casos, o no tem um fi lho no maximo */ if (p->esq == NULL) rp = p->dir; else if (p->dir == NULL) rp = p->esq; else { f=p; rp = p->dir;

s = rp->esq; /* s e sempre o filho esq de rp */ while (s != NULL) {

f = rp; rp = s; s = rp->esq; }

(15)

if (f != p) {

/* p nao e o pai de rp e rp == f->left */ f->esq = rp->dir;

/* remove o no rp de sua atual posicao e o substitui pelo filho direito de rp rp ocupa o lugar de p

*/

rp->dir = p->dir; }

/* define o filho esquerdo de rp de modo que rp ocupe o lugar de p

*/

rp->esq = p->esq; }

/* insere rp na posicao ocupada anteriormente por p */ if (q == NULL) tree = rp; else if (p == q->esq) q->esq = rp; else q->dir = rp; free(p); return rp; }

Árvores Árvores Binárias Balanceadas

Uma árvore binária balanceada, chamada de árvore AVL, é uma árvore binária na qual as alturas das duas subárvores de cada um dos nós nunca diferem em mais de 1. O balanceamento de um nó é igual a diferença entre as suas altura esquerda e direita. Portanto, cada nó de uma árvore balanceada tem balanceamento igual a -1, 0 ou 1, dependendo da comparação entre as alturas esquerda e direita. Lembrando que a altura de um nó nda árvore é o número de nós do maior caminho de naté um de seus descendentes. As folhas tem altura 1. Uma árvore binária completa com n>0 nós tem altura mínima, que é igual a 1 + floor(log (n)). A Figura arvbal mostra uma árvore binária balanceada. Os valores dentro do nó são altura do nó e seu balanceamento.

Figura arvbal: Árvore binária balanceada.

Caso a probabilidade de pesquisar uma chave em uma tabela seja a mesma para todas as chaves, uma árvore binária balanceada terá a  busca mais eficiente. Infelizmente o método de inserção em árvores binárias apresentado anteriormente não garante que a árvore  permanecerá balanceada. Como já vimos a estrutura da árvore depende da ordem em que as chaves são inseridas na árvore.

A Figura arvbali mostra possibilidades de inserção na árvore e o que ocorreria com o seu balancea mento. Cada inserção que mantém a árvore balanceada está indicada bor um B e as que desbalanceiam a árvore por um D. Observe que uma árvore se torna desbalanceada quando o nó inserido se torna descendente esquerdo de um nó que tinha anteriormente um balanceamento de 1 ou se ele se tornar 

(16)

descendente direito de um nó que tinha anteriormente balanceamento de -1. Isto é fácil de deduzir, por exemplo, um nó que tinha  balanceamento 1 e recebe um descendente direito aumenta sua altura em 1, portanto aumentando o seu desbalanceamento.

Figura arvbali: Árvore balanceada e suas possibilidades de inserção

Observemos uma subárvore que ser tornará desbalanceada quando ocorrer uma inserção. Vamos supor também que este nó tem um  balanceamento de 1. Neste caso o desbalanceamento ocorrerá se a inserção ocorrer em um nó da direita. A Figura arvins mostra um

exemplo deste caso.

(17)

Observar que o nó A tem balanceamento 1, isto significa que a subárvore da esquerda tem altura não nula e maior em uma unidade que a da direita. Ao inserirmos um nó na subárvore da direita a sua altura aumenta de um e o balanceamento passa para 2. Observar 

também que como o nó mais jovem a se tornar desbalanceado é o A, o seu filho pela esquerda tem de ter balalneamento igual a 0. Para manter a árvore balanceada é necessário que a transformação na árvore de tal modo que:

a árvore permaneça uma árvore de busca binária; 1.

a árvore continue a ser uma árvore balanceada. 2.

Para isto vamos definir a operação de rotação em uma árvore. Uma rotação pode ser à direita ou esquerda. A Figura arvrot mostra uma árvore e os resultados dos dois tipos de rotação sobre esta árvore estão mostrados na Figura arvrota.

Figura arvrot: Árvore original antes da rotaçã o

Figura arvrota: Efeitos das rotações

(18)

q = p->direita; temp = q->esquerda; q->esquerda = p;  p->direita = temp;

Para verificar o que fazer em uma árvore T após a inserção de um nóqvamos considerar os vários casos possíveis. Primeiro, se após a inclusão todos os nós se mantiveram regulados, então a árvore se mant eve AVL e nada há a fazer. Caso contrário vamos considerar o nó pmais próximo das folhas, que se tornou desregulado. A escolha de pé única, pois qualquer subárvore de T que se tornou desregulada deve incluir  p.

Sejamhd (p)ehe(p)as alturas direita e esquerda das subárvores de p, portanto |hd (p)-he(p)| = 2  poisT era AVL.

Temos os seguintes casos: caso 1:hd (p)>he(p)

neste casoqpertence a subárvore esquerda de p. Além disso ppossui o filho esquerdo u<>q, senão pnão estaria desregulado. Sabe-se também quehd (u)<>he(u), pela mesma razão.Para o caso 1 temos duas possibilidades:

caso 1.1

Exercícios

Escreva um programa que crie uma árvore de busca binária a partir de letras lidas do teclado. O programa deve imprimir a árvore nos três modos de percurso.

Solução: arv0301.c 1.

Faça uma função que imprima os nós de uma árvore na sequencia de seus níveis. Solução: niveis.c

2.

Escreva um programa que leia um arquivo que contem varios numeros inteiros (cada numero em uma linha) e imprima os numeros em ordem crescente (utilize uma arvore para armazenar os numeros na memoria.

Solução: ordem.c 3.

Referências

Documentos relacionados

A porta de alta SEGURANÇA, de DUPLO BATENTE da PORTRISA, permite ocupar vãos mais amplos com o mesmo nível de segurança de qualquer porta START ou MAX, a folha passiva inclui

Vou expor, na medida do possível, parte da conversa na qualificação que considero interessante para 

Para obter detalhes sobre como montar a unidade principal na parede, consulte as instruções incluídas com o suporte para parede1. Entre em contato com seu revendedor local para

O termo extrusão do núcleo pulposo aguda e não compressiva (Enpanc) é usado aqui, pois descreve as principais características da doença e ajuda a

Para o acompanhamento das atividades o aluno deve fazer uso de um computador, devendo este possuir programa para edição de texto e imagem (Microsoft Word e/ou

Os jogos nos ajudam a definir o profissional mais adequado para o perfil do cargo, de forma mais rápida e eficaz, uma vez que, nos permite visualizar seu desempenho em

Para esse fim, analisou, além do EVTEA, os Termos de Referência (TR) do EVTEA e do EIA da Ferrogrão, o manual para elaboração de EVTEA da empresa pública Valec –

Requiring a realignment of the EVTEA with its ToR, fine-tuning it to include the most relevant socio-environmental components and robust methodologies for assessing Ferrogrão’s impact