Post on 05-Dec-2018
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.
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
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; }
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
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
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: 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
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
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
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
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
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 }