Árvores - cavmelo.files.wordpress.com · estrutura de Árvores v uma árvore é composta por um...

83
Árvores Prof. César Melo ICOMP/UFAM

Upload: vutuong

Post on 05-Dec-2018

221 views

Category:

Documents


0 download

TRANSCRIPT

Árvores

Prof. César Melo ICOMP/UFAM

Introdução v  Árvore é uma estrutura adequada para representar

hierarquias §  Diretórios em um computador §  Serviço de resolução de nomes na Internet

v  A forma mais natural para definirmos uma estrutura de árvore é usando recursividade

Estrutura de Árvores v  Uma árvore é composta por um conjunto de nós. v  Existe um nó r, denominado raiz, que contém zero

ou mais sub-árvores, cujas raízes são ligadas a r. v  Esses nós raízes das sub-árvores são ditos filhos do

nó pai, no caso r. v  Nós com filhos são comumente chamados de nós

internos e nós que não têm filhos são chamados de folhas, ou nós externos.

Estrutura de Árvores

Por adotarmos essa forma de representação gráfica, não representamos explicitamente a direção dos ponteiros, subentendendo que eles apontam

sempre do pai para os filhos.

ÁRVORES BINÁRIAS

Definição v  Cada nó interno tem

até dois descendentes (filhos)

v  Os filhos de um nó interno formam um par ordenado §  A sub-árvore da

direita (sad) e §  A sub-árvore da

esquerda (sae).

Altura de uma AB

v  Podemos definir a altura de uma árvore como

sendo o comprimento do caminho mais longo da raiz até uma das folhas.

v  A altura de uma árvore com um único nó raiz é zero e, por conseguinte, dizemos que a altura de uma árvore vazia é negativa e vale -1.

Propriedade: Só existe um caminho da raiz para qualquer nó.

Calculando Altura da AB

v  Seja r a raiz de uma árvore binária A, então a altura de A é dada por

§  Se A != NULL

•  Altura(A) = max{Altura(A.sae),Altura(A.sad)}+1 §  Se A == NULL

•  Altura(A) = -1

Dicionários 8

Número máximo de nós

Propriedade: Em um nível d existem no máximo 2d nós

Dicionários 9

•  Nível conjunto de nós com a mesma profundidade

•  Seja Mn(.) a função que denota a quantidade máxima de elementos em um dado nível d.

•  Claramente, Mn(0) = 1 e •  Mn(d) = 2*Mn(d-1) para d > 0

Uma AB cheia

Uma árvore binária T é dita CHEIA se todos os nós, com exceção das folhas, têm dois filhos.

Dicionários 10

Uma AB cheia

Propriedade: Um árvore binária cheia de altura h tem no máximo (2h+1-1) nós

Dicionários 11

n = Mn(0) + Mn(1) + … + Mn(h) = 20 + 21 + 22 + … + 2h = (1- 2h+1)/(1-2) = 2h+1-1

Uma AB Completa

v  Uma árvore binária T com n níveis é completa se todos os níveis, exceto o último, são completos e o último nível tem todos os seus nós dispostos mais a esquerda

Dicionários 12

TAD Árvore Binária

v  Deve estender um TAD árvore, e herdar todos os seus métodos: §  Construtor, destrutor, altura, tamanho

v  Métodos adcionais §  Sae(r): retorna o nó raiz da sub-árvore a esquerda §  Sad(r): retorna o nó raiz da sub-árvore a direita §  Pai(r): retorna o nó pai do nó passado

Dicionários 13

r

AB: em um vetor v  A indexação – para todo nó v da árvore T, o

índice de v, p(v), será definido da seguinte forma: §  Se v é a raiz: p(v) = 0 §  Se v é o filho a esquerda do nó u: p(v) = 2p(u) + 1 §  Se v é o filho a direita do nó u: p(v) = 2p(u) + 2

Dicionários 14

0

2 1

3 4 5 6

A B C D E F G 0 1 2 3 4 5 6

v  Vantagens: §  Implementação Simples; §  Acesso direto;

AB: em um vetor

v  Custo baseado no espaço – considere a seguinte notação §  n – número de nós na árvore T §  Pm – maior valor de p(v) §  N – tamanho do vetor, i.e, o espaço reservado

v  Melhor cenário: árvore completa e balanceada §  Todas as entradas no vetor estão ocupadas

•  N = Pm+1 = n = O(n)

Dicionários 15

0

2 1

3 4 5 6

AB: em um vetor

v  Pior cenário: árvore desbalanceada em alto grau §  Poucas entradas ocupadas

Dicionários 16

0

1 2

5 6

13 14

Altura: h = (n-1)/2 Max p(v): Pm= 2h+1-2 = 2(n+1)/2-2 Tamanho do vetor: N = Pm+1 = 2(n+1)/2-1=O(2n)

AB no vetor: desempenho

Método Custo Elementos O(n) Troca de elementos, atualizar O(1) Raiz, pai, filhos (sae, sad) 0(1) Raiz da Sae, raiz da sad O(1) É nó interno(externo), É raiz O(1)

Dicionários 17

•  Tempo de execução: Bom! •  Tempos constante para a grande maioria das

operações

•  Espaço: Ruim! •  Melhor cenário: árvore completa O(n) •  Pior cenário: árvore debalanceada O(2n)

AB em uma Estrutura Encadeada

v  Nó na estrutura encadeada da AB 1.  Referência para informação 2.  Referência para o pai 3.  Referência para a sae 4.  Referência para a sad

v  Se o nó é a raiz §  Referência para o pai é NULL

v  Se o nó é uma folha §  Sae e Sad são NULL

Dicionários 18

pai

sad sae info

AB em uma estrutura Encadeada

Dicionários 19

Representação em C typedef struct no TNo; struct no { void* info; TNo* esq; TNo* dir; TNo* pai; };

Criando Nós

TNo* criarNo(TNo* pai, void *c) { TNo* p=malloc(sizeof(TNo)); p->info = c; p->esq = NULL; p->dir = NULL; p->pai = pai; return p; }

Árvore Vazia

short vazia(TNo* a) { return a==NULL; }

Buscando um Elemento

int buscar (TNo* a, void *chave){ if (vazia(a)) return 0; /* não encontrou */ else{ Tcomparavel *e = a->info; return (e->compara(e,chave) == 0) || buscar(a->esq,chave) || buscar(a->dir,chave); } }

Essa função tem como retorno um valor booleano (um ou zero) indicando a ocorrência ou não da informação na árvore.

Impressão (pré-ordem)

static void imprimir (TNo* raiz) { if (raiz==NULL) return print(raiz->info); imprimir(raiz->esq); imprimir(raiz->dir); }

Destrutor

O único cuidado que precisamos tomar na programação dessa função é a de liberar as subárvores antes de liberar

o espaço associado a um nó (isto é, usar pós-ordem).

void destruir (TNo* a) { if (vazia(a)) return destruir(a->esq) destruir(a->dir) free(a) }

Árvore Binária de Busca

v  Seja r um nó QUALQUER de um árvore binária T, então §  Se o descendente direto de

r na sub-árvore a esquerda for menor

§  Se o descendente direto de r na sub-árovre a direita for maior

v  T é uma árvore binária de busca.

Dicionários 2-26

Exemplos

Dicionários 27

Operações

v  Dado uma árvore BB T, as seguintes operações podem atuar sobre T §  Tamanho §  Altura §  Inserir, Remover, Buscar, Atualizar

Dicionários 28

Inserir

v  Caminha comparando o valor a ser inserido com os valores na árvore.

v  Encontrada a posição: §  Atualiza o pai do novo nó; §  Atualiza o endereço da raiz

da sub árvore definida pelo novo no;

Dicionários 29

20

Legenda:

Pai SAD

NOVO

Inserir

Dicionários 30

Remover

v  Três cenários §  Remoção de Folha §  Remoção de nó com apenas um descendente §  Remoção de nó com dois descendente

Dicionários 31

Remover: Folha

Dicionários 32

4

Legenda:

Caminho de busca

Remover: Raiz com UM filho

Dicionários 33

14

Legenda:

Caminho de busca

Remover: Raiz com dois filhos

Dicionários 34

8

Legenda:

Caminho de busca

1)  Encontra o maior da SAE 2)  Troca o Maior da SAE com

a raiz sendo removida 3)  Retoma a remoção na SAE

8

7

Até mais ABB, muito prazer AVL. Escreva sobre a estrutura de dados Árvore Binária de Busca(ABB). Você terá 10 minutos para finalizar a tarefa. Identifique o material produzido, e o coloque na mesa do professor.

Inserção

Propriedade

Pós-fixada Altura

Predecessor

Maior

Pré-fixada

Remoção Custo

Degenerada Sucessor

Representação em C Recursividade

Raiz

Dicionário

Sub-árvore

Menor

In-fixada Descendente

Menor

Pai

Caminho de busca

Contexto l  O pior que pode acontecer a uma ABB é a sua

degeneração. •  Perder as qualidades ou características primitivas; • Alterar-se para pior; Estragar-se Corromper-se

l  O pior cenário:

1

4

5

3 2

Introdução l  Árvore Balanceada

• Uma árvore binária balanceada é aquela em que, para qualquer nó, suas sub-árvores esquerda e direita têm a mesma altura.

l  Menos restritiva é uma Árvore AVL • Uma árvore binária de busca é considerada balanceada

quando, para cada nó, as alturas de suas sub-árvores esquerda e direita diferem de, no máximo, UMA unidade.

•  Essa diferença é chamada fator de balanceamento, ou FB(n).

Introdução

l  Seja um nó n qualquer da árvore: • FB(n) = altura(sad) – altura(sae). • se FB(n) = 0, as duas sub-árvores têm a

mesma altura; • se FB(n) = -1, a sub-árvore esquerda é mais

alta que a direita em UMA unidade; • se FB(n) = +1, a sub-árvore direita é mais alta

que a esquerda em UMA unidade.

Introdução

Exemplos de Árvores AVL

Introdução

Exemplos de Árvores Binárias MAS que não são AVL

Introdução

l  A vantagem de uma árvore AVL sobre uma degenerada está na maior eficiência nas suas operações de busca, pois, sendo a altura da AVL bem menor, o número necessário de comparações diminui sensivelmente.

l  Por exemplo, numa árvore degenerada de 10.000 nós, são necessárias, em média, 5.000 comparações, numa busca; numa árvore AVL, com o mesmo número de nós, essa média baixa para 14.

l  A solução é adotar um algoritmo que, a cada inserção, faça as correções necessárias para manter sempre a árvore como uma árvore AVL, ou seja, onde qualquer nó n tenha |FB(n)| <= 1.

Balanceamento l  Como manter a propriedade da AVL?

•  Insere-se um novo nó na árvore; • Caso a inserção do novo nó viole a propriedade de

balanceamento; •  É preciso restaurar o balanço da árvore.

• O balanço é mantido por meio de ROTAÇÕES na árvore.

Balanceamento, preparação...

l  Serão usados dois ponteiros A e B, para auxiliar: • A é nó ancestral mais próximo do nó inserido com FB(nó) ≠ 0 antes da inserção, ou a própria raiz se não há nenhum nó com FB(nó) ≠ 0 (antes da inserção) no caminho da busca.

• A é também chamado de Pivô; • B é filho de A na sub-árvore onde ocorreu a

inserção.

Exemplo de Desbalanceamento

10

5

(-1)

(0)

Árvore onde ocorre A inserção do valor 2

10

5

2

(-2)

(-1)

(0)

Após a Inserção do valor 2

A

B

Quem é A e quem é B?

novo nó inserido

Inserção Balanceada l  Há 4 casos para serem analisados. l  Solução rotação simples:

•  Inserção na sub-árvore esquerda do filho esquerdo de A •  Inserção na sub-árvore direita do filho direito de A

l  Solução rotação dupla: •  Inserção na sub-árvore esquerda do filho direito de A •  Inserção na sub-árvore direita do filho esquerdo de A

Rotação Simples

proc rotação simples se FB(A) = +2 então rotação simples à esquerda senão rotação simples à direita fim se zera fatores de A e de B fim proc

Rotação Simples à Esquerda

A->dir = B->esq;

10

20

30

A

B (+2)

(+1)

(0)

10

20

30

A

B 20

30

B

10 A

B->esq = A;

0

1

2

Rotação Simples à Direita

A->esq = B->dir;

B->dir = A;

5

10

B

2

A

A

B 10

5

2

(-2)

(-1)

(0)

A 10

B 5

2

0

1

2

Inserção - Exemplo l  Mostrar as rotações necessárias para a construção da

seguinte árvore AVL: 3, 2, 1, 4, 5, 6 e 7

3

2

1

Quem é A? Quem é B? Quais os FB’s? O que é necessário fazer para equilibrar essa árvore?

A

B

(-2)

(-1)

(0)

Inserção - Exemplo

2

1 3

O resultado da rotação à direita fica...

Após a inserção de 4 e 5 fica... 2

1 3

4

5

O que tem que ser feito para re-equilibrar?

Inserção - Exemplo O resultado da rotação à esquerda fica...

2

1 4

5

O que tem que ser feito para re-equilibrar?

3

Mas quando o 6 é inserido o resultado fica... 2

1 4

5 3

6

Inserção - Exemplo O resultado da rotação à esquerda fica...

O que tem que ser feito para re-equilibrar?

Mas quando o 7 é inserido o resultado fica...

4

2 5

6 3 1

4

2 5

6 3 1

7

Inserção - Exemplo O resultado da rotação à esquerda fica...

4

2 6

7 3 1 5

Pensando num Futuro próximo... l  Defina em C uma estrutura de dados que possa

representar uma árvore AVL; l  Implemente procedimento para calcular FB; l  Implemente procedimento que percorra a árvore e

imprima o fator de balanceamento de cado nó em uma ordem in-fixada com o seguinte formato: •  n(FB), onde n é uma raiz de sub-árvore e FB o fator de

balanceamento. l  Implemente os procedimentos de rotação simples.

Exercício

l  Mostrar as rotações necessárias para a construção da seguinte árvore AVL: 5, 4, 3, 6, 7, 8 e 9

Próximo episódio de AVL l  Há 4 casos para serem analisados. ü  Solução rotação simples:

ü  Inserção na sub-árvore esquerda do filho esquerdo de A ü  Inserção na sub-árvore direita do filho direito de A

l  Solução rotação dupla: •  Inserção na sub-árvore esquerda do filho direito de A •  Inserção na sub-árvore direita do filho esquerdo de A

Relembrando e antes de rotacionar duplo

l  A é o primeiro ancestral no caminho de busca cujo o fator de balanceamento, antes da inserção, era diferente de zero;

l  B é o filho de A na sub-árvore onde ocorreu a inserção;

l  E temos agora um novo ator: • Aux que é um filho de B.

Rotação Dupla

proc rotação dupla se FB(A) = +2 então rotação dupla à direita senão rotação dupla à esquerda fim se ajusta fatores dos nós envolvidos na rotação fim proc

Rotação Dupla à Esquerda l  É composta por uma rotação simples á esquerda (B e

Aux) seguida de uma rotação simples à direita (A e Aux)

l  Aux é o filho direito de B.

Rotação Dupla à Esquerda Aux = B->dir; // rotação simples à esquerda (B – Aux) B->dir = Aux->esq; Aux->esq = B;

// rotação simples à direita (A – Aux) A->esq = Aux->dir; Aux->dir = A;

20

10

15

A

B

Aux

20

15

10

Aux

A

B

15

10 20

Aux

A B

Rotação Dupla à Direita l  É composta por uma rotação simples à direita (B e

Aux) seguida de uma rotação simples à esquerda (A e Aux)

l  Aux é o filho esquerdo de B.

Rotação Dupla à Direita Aux = B->esq; // rotação simples à direita (B – Aux) B->esq = Aux->dir; Aux->dir = B;

// rotação simples à esquerda (A – Aux) A->dir = Aux->esq; Aux->esq = A;

25

30

Aux

B

20 A 20

Aux

A

25

B

30 25

30

Aux

A B 20

Rotação Dupla - Exemplo

4

2 6

7 3 1 5

Como ficaria se fosse inserido o valor 16?

Rotação Dupla - Exemplo

Como ficaria se fosse inserido o valor 15?

16

4

2 6

3 1 5 7

A Árvore ainda fica OK!

Rotação Dupla - Exemplo

4

2 6

3 1 5 7

Desequilíbrio no nó 7. Rotação dupla à direita.

16

15

15

16 7

7

15

16

Rotação Dupla - Exemplo

Agora a árvore está OK!

16

4

2 6

3 1 5

7

15

E se inseríssemos o 14?

Rotação Dupla - Exemplo

Desequilíbrio no nó 6. Rotação dupla à direita

16

4

2 6

3 1 5 15

14

7

Primeira fase: Rotação simples à direita

Rotação Dupla - Exemplo

15

4

2 6

3 1 5 7

16 14

Segunda fase: Rotação simples à esquerda

Rotação Dupla - Exemplo

Agora a árvore está OK!

15

4

2 7

3 1 6

16 14 5

DESAFIO G.M. Adelson-Velskii e E.M. Landis

Application Layer 2-71

O problema: Implementar um método/forma de atualização dos fatores de balanceamento de uma árvore. Dica: Considere todos os casos (rotações) e veja como elas afetam o fator de balaceamento. Custo: Em quanto o seu método melhora a desempenho da implementação.

Remoção l  Inicialmente, faz-se a retirada do nó, usando o algoritmo

de busca e retirada de uma ABB. l  Se não desbalanceou, o processo está encerrado. l  Se desbalanceou a árvore, isto é, se um ou mais nós

ficou com |FB(nó)|>1, raciocina-se em termos de inserção, perguntando: •  se o desbalanceamento ocorresse devido a uma inserção, que nó teria

sido inserido para causar tal desequilíbrio?

l  Identificado o nó, simula-se sua inserção e faz-se a rotação necessária.

Remoção

Retirando o 5 resulta uma árvore desbalanceada no nó 10.

Uma rotação simples à esquerda resolve o problema. Que nó inserido teria causado esse desequilíbrio? o 30.

Remoção Retirando o 12 desequilibra a raiz. Podemos supondo que a inserção recente foi o 8.

Uma rotação dupla à esquerda corrige o problema.

Remoção A retirada da folha 2 desbalanceia a raiz 6.

Solução: escolhe-se arbitrariamente um desses dois nós, despreza-se o outro (mantendo-o na árvore, obviamente), e simula-se a sua inserção.

Escolhemos o 12, que exige uma operação mais simples: rotação simples à esquerda.

Essa configuração jamais pode vir de uma seqüência de inserções, pois, se ela fosse 8, 12 ou 12, 8, a primeira dessas inclusões já provocaria rotação.

Remoção Infelizmente, há situações mais complexas, onde o próprio processo de balanceamento devido a retirada de um nó de uma subárvore, pode provocar um novo desequilíbrio na árvore.

A solução será reaplicar o método para a árvore que desbalanceou. E novo desequilíbrio pode ser provocado mais acima, exigindo novo balanceamento. E assim por diante, até que toda a árvore volte a ser uma AVL.

Remoção Isso causará o desequilíbrio da subárvore cuja raiz é 70; aplicando nosso método para esta subárvore apenas, simulamos o ingresso do 90, fazendo uma rotação simples à esquerda entre 70 e 80, o que resulta na árvore abaixo:

Esta árvore é AVL?

Remoção A árvore não é AVL, pois |FB(60)|>1. Temos que reaplicar o método para o 60. Considerando que, neste caso, tanto faz escolhermos o 42, o 52 ou o 56 para ser o nó de inserção simulada, a rotação exigida é a dupla à esquerda (60-40), o resultado é a árvore abaixo que, finalmente, é uma AVL:

Como último comentário...

Isso mostra porque a remoção é mais complicada que a inserção. Enquanto nesta operação, no máximo uma rotação (simples ou dupla) servirá para manter a árvore balanceada, na remoção de um único nó, mais

de uma rotação poderá ser necessária.

Algoritmo: (Parte I)

v TArv* poda (TArv* r, int v){

v  if (r == NULL) v  return NULL;

v  else if (r->info > v)

v  r->esq = poda(r->esq, v); v  // mesmo procedimento inserção

v  else if (r->info < v) v  r->dir = poda(r->dir, v);

v  // mesmo procedimento inserção v  else { /* achou o elemento */

v  // Próximo Slide

v  } v  return r;

v }

Algoritmo: (Parte II)

v  else { /* achou o elemento */ v  if (r->esq == NULL && r->dir == NULL){} /* Folha? */

v  else if (r->esq == NULL){} /* Só tem filho à direita? */

v  else if (r->dir == NULL){} /* Só tem filho à esquerda? */ v  else { /* Tem os dois filhos */

v  /* encontra sucessor */ & /* troca as informações entre raiz e sucessor*/ v  r->dir = poda(r->dir,v);

v  //mesmo procedimento v  }

Algoritmo: (Parte II)

v  else { /* achou o elemento */ v  if (r->esq == NULL && r->dir == NULL){} /* Folha? */

v  else if (r->esq == NULL){} /* Só tem filho à direita? */

v  else if (r->dir == NULL){} /* Só tem filho à esquerda? */ v  else { /* Tem os dois filhos */

v  /* encontra sucessor */ & /* troca as informações entre raiz e sucessor*/ v  r->dir = poda(r->dir,v);

v  //mesmo procedimento v  }

Pior Cenário da ABB

v  As chaves inseridas de forma ordenada: §  Crescente §  Decrescente

Dicionários 83