INE 5408 Estruturas de Dados rvores AVL Caractersticas















































- Slides: 47
INE 5408 Estruturas de Dados Árvores AVL • Características • Rotações • Algoritmos
Histórico • Inventada em por Georgy Maximovich Adelson-Velskii (Гео ргий Макси мович Адельсо н. Ве льский) e Yevgeniy Mikhailovich Landis (Евгений Михайлович Ландис) e publicada em: "An algorithm for the organization of information. Trans. of Akademiya Nauk SSSR. Doklady, 1962, v. 146, no. 2, pp. 263 -266 " Georgy Maximovich Adelson-Velskii (Samara, Rússia, 1922) Yevgeniy Mikhailovich Landis (Kharkov, Ucrânia, 1921)
Características • Manter uma árvore binária de busca balanceada sob a presença de constantes inserções e deleções é ineficiente. • Para contornar esse problema foi criada a árvore AVL (Adelson-Velskii e Landis). • A árvore AVL é uma árvore binária com uma condição de balanço, porém não completamente balanceada. • Árvores AVL permitem inserção / deleção e rebalanceamento aceitavelmente rápidos.
Características Critério de balanceamento: • dado um nodo qualquer, uma árvore está AVL-balanceada se as alturas duas subárvores deste nodo diferem de, no máximo, 1. Para rebalancear uma árvore após uma inserção, são utilizadas rotações de subárvores: • rotações simples em muitos casos; • rotações duplas em alguns.
Características Estrutura de um nodo na árvore AVL Estrutura nodo. AVL { info : tipo_info; esq : *nodo. AVL; dir : *nodo. AVL; alt : inteiro; }; • O campo alt deve ser atualizado recursivamente quando um nodo for inserido ou deletado.
Características • Para o retorno da altura de uma subárvore necessitamos de uma função especial – um nodo pode não ter um ou ambos os filhos – embre-se que a pesquisa para garantir a condição-AVL é realizada perguntando-se a altura das duas subárvores inteiro altura( subárvore *nodo. AVL) início se (subárvore for NULO) retorne -1; /* A altura de uma subárvore inexistente é definida como -1 */ senão retorne subárvore->alt; fim se fim
Inclusão com rotação Exemplo de rebalanceamento: • o nodo com chave 6. 5 desequilibrou a árvore. Com a rotação da subárvore em torno do nodo 7, rebalanceamos. 6 6 2 1 4 3 7 6, 5 7 2 8 1 4 3 6, 5 8
Inclusão com rotação Algoritmo básico: • partimos do nodo inserido e subimos a árvore; • atualizamos a informação do balanceamento em cada nodo (na árvore AVL, cada nodo conhece a sua altura); • se chegamos à raiz sem encontrar nada errado, terminamos; • caso encontremos um nodo desbalanceado (|altesq - altdir| < 2 ferida), realizamos a rotação no primeiro nodo desbalanceado encontrado. • No exemplo anterior, isto significa que, depois da inserção de 6. 5, o nodo 8 ficou desbalanceado.
Exemplo: criação de uma árvore AVL com os números de 1 a 7 O primeiro problema ocorre quando inserimos o 3. A condição-AVL foi violada. • executamos uma rotação simples entre a raiz (cuja condição-AVL está violada) e seu filho da direita. 1 2 3 3
Exemplo: criação de uma árvore AVL com os números de 1 a 7 Inserimos o elemento 4 sem problemas. Inserimos o elemento 5: violação em 3. • Mesmo processo da rotação anterior será seguido. • Importantíssimo: observe-se que 2 precisa ser notificado que seu filho agora é o nodo com chave 4. 2 1 3 4 4 3 5 5
Exemplo: criação de uma árvore AVL com os números de 1 a 7 A inclusão do elemento 6 desequilibra a raiz: • subárvore direita tem altura 0; • subárvore esquerda tem altura 2. Rotacionamos na raiz entre 2 e 4: • transformamos 2 em filho de 4; • subárvore esquerda original de 4 é agora subárvore direita de 2. Condição de ordem da árvore de busca é mantida.
Exemplo: criação de uma árvore AVL com os números de 1 a 7 A inclusão de um nodo com chave 7 causa uma rotação como já havíamos visto antes: • 5 fica desequilibrado; • rotacionamos em 6.
Inclusão com rotação simples à esquerda • Corresponde a uma rotação entre os nodos k 2 e seu filho da esquerda, k 1, envolvendo as subárvores A, B e C Devolvo k 1 como a nova raiz desta subárvore Esta aresta é o ponto de desequilíbrio k 2 k 1 A B C
Inclusão com rotação simples à esquerda nodo. AVL *simp_roda_esq(k 2 *nodo. AVL) variáveis locais k 1 : *nodo. AVL; início k 1 <- k 2 ->esq; k 2 ->esq <- k 1 ->dir; k 1 ->dir <- k 2; /* atualize alturas */ k 2 ->alt <- max(altura(k 2 ->esq), altura(k 2 ->dir)) + 1; k 1 ->alt <- max(altura(k 1 ->esq), k 2 ->alt) + 1; retorne k 1; /* nova raiz da subárvore */ fim
Inclusão com rotação simples à direita nodo. AVL *simp_roda_dir(k 2 *nodo. AVL) variáveis locais k 1 : *nodo. AVL; início k 1 <- k 2 ->dir; k 2 ->dir <- k 1 ->esq; k 1 ->esq <- k 2; /* atualize alturas */ k 2 ->alt <- max(altura(k 2 ->dir), altura(k 2 ->esq)) + 1; k 1 ->alt <- max(altura(k 1 ->dir), k 2 ->alt) + 1; retorne k 1; /* nova raiz da subárvore */ fim
Inclusão com rotação dupla O algoritmo descrito até agora tem um problema: • existem casos onde ele não basta para consertar a árvore após uma inclusão ou exclusão. Exemplo: inserimos as chaves 8 a 15, em ordem inversa, na árvore anterior. • a inserção de 15 não provoca problemas, nem desbalanceia a árvore; • a inserção de 14, por sua vez, faz o rebalanceamento falhar.
Inclusão com rotação dupla O problema que surge aqui é que tanto 7 como 14 são candidatos à subárvore esquerda de 15. Neste caso, necessitamos de uma dupla rotação.
Inclusão com rotação dupla • O processo é semelhante à rotação simples, só que envolve 4 subárvores: A, B, C e D • corresponderia a uma rotação efetuada primeiro entre k 1 e k 2 e depois entre k 2 e k 3 k 2 k 1 k 3 A k 1 k 2 D B C A B C D
Inclusão com rotação dupla • P 1: Rotação entre k 1 e k 2 k 3 k 1 A k 2 D B C
Inclusão com rotação dupla • P 2: Rotação entre k 2 e k 3 Devolvo k 2 como a nova raiz desta subárvore k 3 k 2 A k 1 B C D
Rotação dupla à esquerda • Exemplo simétrico ao anterior. k 3 k 2 k 1 k 3 k 1 A k 2 D D C B A
Inclusão do elemento 14 com rotação dupla No exemplo, a rotação direita-esquerda envolve o 7, o 15 e o 14: • k 3 é o nodo de chave 7, k 1 o de chave 15 e k 2 o de chave 14; • primeiro rotacionamos 14 -15 à direita, depois 7 -14 à esquerda.
Inclusão do elemento 13 com rotação dupla Novamente uma dupla rotação direitaesquerda: • desta vez a rotação envolverá 6 (k 3), 14 (k 1) e 7 (k 2).
Inclusão do elemento 12 A inclusão do elemento 12 provoca um desequilíbrio na raiz: • como 12 não está entre 4 e 7, sabemos que uma rotação simples vai funcionar.
Inclusão do elemento 11 A inclusão do elemento 11 provoca um desequilíbrio logo embaixo: • como 11 não está entre 12 e 13, sabemos que uma rotação simples vai funcionar.
Inclusão dos elementos 10, 9 e 8 Os elementos 10 e 9 serão inseridos com rotação simples, e o elemento 8 sem rotação alguma. • Tente em casa A árvore resultante será vista a seguir.
Caso interessante: inclusão do elemento 8. 5
Temos uma rotação dupla que parece ser uma simples
Rotação Dupla à Esquerda • Função chamada somente se k 3 possuir um filho à esquerda e se o filho à esquerda de k 3 possuir um filho à direita. Ela realiza a rotação e atualiza as alturas das subárvores rotacionadas. nodo. AVL *dup_roda_esq(k 3 : *nodo. AVL) início /* Rotacione entre k 1 e k 2 */ k 3 ->esq <- simp_roda_dir( k 3 -> esq ); /* Rotacione entre k 3 e k 2 */ retorne ( simp_roda_esq( k 3 ); fim • Da mesma forma que com a rotação simples, a rotação dupla à direita é a operação simétrica da rotação à esquerda e é facilmente implementada.
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); senão arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então pai->esq <- arv_rodada; senão pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua)
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); senão arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então pai->esq <- arv_rodada; senão pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua) Novidade na Árvore AVL
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); senão arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então pai->esq <- arv_rodada; senão pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua) Verifica se vai rodar
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); Verifica o tipo de senão rotação e roda arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então pai->esq <- arv_rodada; senão pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua)
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); senão arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então Faz pai apontar pai->esq <- arv_rodada; para k 2 senão pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua)
Inserção em Árvore AVL nodo. AVL *inserção. AVL(info : tipo_info, arv : *nodo. AVL, pai : *nodo. AVL) variáveis arv_rodada : *nodo. AVL; início se (arv é NULO) então /* Folha: aloca novo nodo */ arv <- aloca novo nodo; se (arv é NULO) então retorne ERRO; arv->info <- info; arv->alt <- 0; arv->esq <- NULO; arv->dir <- NULO; senão se (info < arv->info) então arv->esq <- inserção. AVL(info, arv->esq, arv); se ((altura(arv->esq) - altura(arv->dir) > 1) se (info < arv->esq->info) então arv_rodada <- simp_roda_esq(arv); senão arv_rodada <- dup_roda_esq(arv); fim se se (pai->esq = arv) então pai->esq <- arv_rodada; Ajusta altura deste senão nodo aqui pai->dir <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão (continua)
Inserção em Árvore AVL senão (continuado de cima) se (info > arv->info) então /* caso simétrico para árvore direita */ arv->dir <- inserção. AVL(info, arv->dir, arv); se ((altura(arv->dir) - altura(arv->esq) > 1) se (info < arv->dir->info) então arv_rodada <- simp_roda_dir(arv); senão arv_rodada <- dup_roda_dir(arv); Caso simétrico: fim se lembre de se (pai->dir = arv) então espelhar! pai->dir <- arv_rodada; senão pai->esq <- arv_rodada; fim se senão arv->alt <- max( altura(arv->esq), altura(arv->dir)) + 1; fim se senão retorne ERRO: “chave já está na árvore" fim se retorne arv; fim
Deleção em Árvore AVL • Busque a chave a deletar na árvore • Delete utilizando o algoritmo recursivo de Deleção da Árvore Binária de Busca (DABB) Visto em aula • Modifique DABB para: – Logo após retorno da chamada recursiva atualizar & verificar alturas das subárvores filhas – Aplicar “regra do zique-zague” se necessário
Deleção em Árvore AVL • “regra do zigue-zague”: – Olhar subárvore que desequilibrou e seus filhos – Desequilíbrio é à esquerda: Analisar filhos e • Sub-subárvore esquerda é a mais funda? netos do nodo – Esquerda-esquerda: rotação simples à esquerda desequilibrado • Sub-subárvore direita é a mais funda? – Esquerda-direita: rotação dupla à esquerda – Desequilíbrio é à direita: • Sub-subárvore direita é a mais funda? – Direita-direita: rotação simples à direita • Sub-subárvore esquerda é a mais funda? – Direita-esquerda: rotação dupla à direita
Rebalanceamento após exclusão com rotação simples à direita • Direita-direita: Rotação entre k 2 e k 3 Desequilíbrio identificado em k 3 Subárvore Devolvo k 1 como causadora é a nova raiz desta direita (k 1) subárvore Sub-subárvore mais funda é direita (k 2) k 3 k 1 A k 2 B C D
Rebalanceamento após exclusão com rotação dupla à direita • P 1: Rotação à esquerda entre k 1 e k 2 Desequilíbrio identificado em k 3 Subárvore causadora é direita (k 1) k 3 Sub-subárvore mais funda é esquerda (k 2) k 1 A k 2 D B C
Rebalanceamento após exclusão com rotação dupla à direita • P 2: Rotação à direita: Rotação entre k 2 e k 3 Devolvo k 2 como a nova raiz desta subárvore k 3 k 2 A k 1 B C D
Resumindo: regras de deleção (à esquerda) Esquerda-esquerda k 3 k 1 / / k 1 A rot. simples à esq. k 2 k 3 k 1 / - - - - -> / k 2 B D C B A / D C Esquerda-direita k 3 k 2 / / k 1 T 4 rot. dir. (k 1) k 2 T 4 rot. esq. (k 3) k 1 k 3 k 2 / - - -> / T 1 k 2 k 1 T 3 T 1 T 2 T 3 T 4 k 2 / T 2 T 3 T 1 T 2
Resumindo: regras de deleção (à direita) Direita-direita k 3 k 1 / / A k 1 rot. simples à dir. k 3 k 2 k 1 / - - - - -> / B k 2 A B C D / C D Direita-esquerda k 3 k 2 / A k 1 rot. esq. (k 1) A k 2 rot. dir. (k 3) k 3 k 1 k 2 / - - -> / k 2 D B k 1 A B C D k 2 / / B C D
Relembrando: Algoritmo de deleção t. Nodo* FUNÇÃO delete(info: t. Info, arv: *t. Nodo) tmp, filho: *t. Nodo; início se (arv = NULO) então retorne arv senão se (info < arv->info) // Vá à esquerda. arv->filhoÀEsquerda <- delete(info, arv->filhoÀEsquerda); retorne arv; senão se (info > arv->info) // Vá à direita. arv->filhoÀDireita <- delete(info, arv->filhoÀDireita); retorne arv; arv <- atualiza-zigue-zague(arv); senão // Encontrei elemento quero deletar. (CONTINUA)
Relembrando: Algoritmo de deleção (cont) (CONTINUAÇÃO) se (arv->filhoÀDireita ~= NULO E arv->filhoÀEsquerda ~= NULO) // 2 filhos. tmp <- mínimo(arv->filhoÀDireita); arv->info <- tmp->info; arv->filhoÀDireita <- delete(arv->info, arv->filhoÀDireita); arv <- atualiza-zigue-zague(arv); retorne arv; senão // 1 filho. tmp <- arv; se (arv->filhoÀDireita ~= NULO) então // Filho à direita. filho <- arv->filhoÀDireita; retorne filho; senão se (arv->filhoÀEsquerda ~= NULO) então // Filho à esquerda. filho <- arv->filhoÀEsquerda; retorne filho; senão // Folha. libere arv; retorne NULO; fim se fim se fim DABB não ajusta alturas! Lembre de sempre atualizar alturas antes de calcular o “zigue-zague”
Funciona? • Exercício em sala: Exemplos no simulador – https: //www. cs. usfca. edu/~galles/visualization/AVLtree. html
Atribuição-Uso Não-Comercial-Compartilhamento pela Licença 2. 5 Brasil Você pode: - copiar, distribuir, exibir e executar a obra - criar obras derivadas Sob as seguintes condições: Atribuição — Você deve dar crédito ao autor original, da forma especificada pelo autor ou licenciante. Uso Não-Comercial — Você não pode utilizar esta obra com finalidades comerciais. Compartilhamento pela mesma Licença — Se você alterar, transformar, ou criar outra obra com base nesta, você somente poderá distribuir a obra resultante sob uma licença idêntica a esta. Para ver uma cópia desta licença, visite http: //creativecommons. org/licenses/by-nc-sa/2. 5/br/ ou mande uma carta para Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.