AED Algoritmos e Estruturas de Dados LEEC - 2004/2005 Árvores Árvores - Introdução (1) As árvores são estruturas de dados usadas em diversas aplicações na vida comum: Bases de dados de grande dimensão. Reconhecimento de frases geradas por linguagens (ex: programas, expressões aritméticas,...). Modelação de sistemas organizacionais (ex: famílias, directórios de um computador, hierarquia de comando de uma empresa,...). Determinação do caminho mais curto entre dois computadores de uma rede. AED (IST/DEEC) 2
Árvores - Introdução (2) Def: Uma árvore é um caso especial de um grafo, i.e., um par (V,E) de dois conjuntos não vazios V-nós (vertex) e Ε V arestas (edges) que satisfazem duas condições: Entre dois nós existe um único caminho (um caminho (path) é uma sequência de arestas entre dois nós), Um único nó, denominado raiz (root), só existe como primeiro elemento nos pares de E: os restantes nós são um segundo elemento dos pares de E (podendo também ser primeiro elemento de alguns pares). AED (IST/DEEC) 3 Árvores - Introdução (3) Def: se (v 1,v 2 ) E, v 1 é nó ascendente (parent) e v 2 é nó filho (child). A raiz é o único nó sem ascendente. Nós sem filhos são designados por folhas (leaves). Nós com filhos são designados não-terminais. Nós não-terminais, distintos da raiz, são designados intermédios. Def: A árvore é de tipo K, sse todos os nós intermédios tiverem K filhos. Quando K=2, a árvore diz-se binária. Def: O nível (level) de um nó é o número de arestas entre a raiz e o nó (raiz tem nível 0). A altura (heigh) de uma árvore é o máximo número de nós encontrados nos caminhos até às folhas (uma árvore só com a raiz tem altura 1). AED (IST/DEEC) 4
Árvores - Introdução (4) Representação gráfica das árvores adopta a convenção: raiz no topo, nós representados por círculos, arestas representadas por linhas (ou setas no sentido da raiz para a folha). Exemplo A1: directórios do sistema operativo Linux / bin dev etc home usr var cfb lmv rgc AED (IST/DEEC) 5 Árvores - Introdução (5) Exemplo A2: expressões aritméticas 2+3*5 + n 1 2 * n 2 n 3 (2+3)*5 + * raiz 5 folha 3 5 n 4 n 5 2 3 V={n 1,n 2,n 3,n 4,n 5 } E={(n 1, n 2 ),(n 1,n 3 ),(n 3,n 4 ),(n 3,n 5 )} A árvore é avaliada de forma ascendente (bottom-up): 2+3*5 = 2+15 = 17 Parêntesis, usados nas expressões aritméticas para ultrapassar as prioridades dos operadores, são indicados na árvore através da posição dos nós. AED (IST/DEEC) 6
Árvores - Introdução (6) Exemplo A3: árvore de pesquisa binária 1 12 n 5 17 2 8 31 <n >n Subárvore direita (esquerda) armazena números maiores (menores) que o nó. Se os dados forem cadeias de caracteres, usa-se a ordem lexicográfica (cf>cb e cf>az ). Vantagens: pesquisa mais rápida-o(log N) Inconvenientes: pode degenerar lista com pesquisa-o(n), a resolução deste problema torna inserção mais complicada 1 BST - Binary Search Tree AED (IST/DEEC) 7 Árvores - Introdução (7) Uma árvore de ordem K pode ser transformada numa árvore binária, com referências esquerdas contêm sucessivamente as sub-árvores da árvore K referências direitas contém referência para a parte restante da árvore K Exemplo A4: transformação de uma árvore ternária numa árvore binária a b c a b AED (IST/DEEC) 8 c
Árvores - Introdução (8) As árvores BB possuem, em cada nó, uma tabela para as subárvores. São usadas em Bases de Dados. a b c A B C D A < a < B < b < C < c < D Todos os caminhos da raiz para as folhas possuem o mesmo comprimento AED (IST/DEEC) 9 Árvores - Introdução (9) Em AED/LEEC são estudadas apenas árvores BST(binary search tree): Representação de uma árvore binária em C Propriedades Operações: Inserção de um elemento numa árvore Directa Balanceada AVL RB Combinação de duas árvores Retirada de um elemento Percurso de uma árvore AED (IST/DEEC) 10
Árvores - Representação em C Seja a definição em C de uma árvore binária, contendo cada nó um dado de tipo data. typedef data; typedef struct _s1 { data item; /* dados */ struct _s1 *left, *right; /* referência filhos */ } node; node *root = NULL; /* raiz (árvore inicial é vazia) */ AED (IST/DEEC) 11 Árvores - Propriedades (1) Teorema A1: Uma árvore binária com N nós não-terminais possui N+1 folhas. Estratégia de prova: indução, com base na construção da árvore a partir de duas subárvores. Se N=0, a árvore possui um único nó (que é raiz e folha em simultâneo). Seja N>0 o número de nós não-terminais: a subárvore esquerda tem k nós, a subárvore direita tem N-k-1 nós (0<k<N+1). Por hipótese, a subárvore esquerda tem k+1 folhas e a subárvore direita tem N-k folhas. Somando, a árvore tem (k+1)+(n-k)=n+1 folhas. QED k N-k-1 AED (IST/DEEC) 12
Árvores - Propriedades (2) Teorema A2: Uma árvore binária com N nós não-terminais possui 2*N arestas (N-1 para os nós não-terminais e N+1 para as folhas). Estratégia de prova: contar o número de arestas para cada nó. Exceptuando a raiz, cada nó possui um único ascendente, pelo que só há uma aresta entre um nó e o seu ascendente. Pelo teorema A1 e observação anterior, há N+1 arestas para as folhas. Pelas duas observações anteriores, há N-1 arestas para os nós intermédios. No total, a árvore com N nós não-terminais possui (N+1)+(N-1)=2*N arestas. QED AED (IST/DEEC) 13 Árvores - Propriedades (3) Teorema A3: Numa árvore binária com N nós, o nível das folhas h l varia entre log 2 N e N-1. Estratégia de prova: identificar níveis máximo e mínimo. QED O nível máximo é o da árvore degenerada numa lista. Neste caso, h l =N-1 arestas. O nível mínimo é o da árvore balanceada, cada nível i com 2 i nós. Por A1 há N+1 folhas, logo 2 h l-1 <N+1 2 h l. Assim, h l é o maior inteiro igual ou inferior a log 2 N (ex: log 2 7 = 2, log 2 8 = 3) AED (IST/DEEC) 14
Síntese da Aula 1 de Árvores Introdução às árvores Representação em C Propriedades básicas de árvores AED (IST/DEEC) 15 Árvores - Inserção (1) Sendo as árvores recursivas, as funções de manipulação podem ser recursivas: a instrução recursiva é aplicada nos nós intermédios e a instrução terminal nas folhas. As árvores são, usualmente, construídas de forma descendente (top-down). Nos algoritmos de inserção consideramos o item um inteiro (typedef int data;) e uma árvore BST. Se a informação a armazenar for uma estrutura, um dos campos designado chave (key) tem de ser tipo ordenado. Ex: no bilhete de identidade, a chave é o número do BI. AED (IST/DEEC) 16
Árvores - Inserção (2) Inserção directa A inserção efectuada em dois casos, conforme situação da árvore: vazia: insere directamente novo dado não vazia: procura, em primeiro lugar, o nó onde inserir o novo dado node *insert(int i, node *tree) { node *upper = tree, *child = tree, *leaf; if (tree == NULL) { /* árvore vazia */ tree = (node *) malloc(sizeof(node)); } tree->item = i; /* insere dado */ tree->left = tree->right = NULL; /* sub-árvores vazias */ return tree; AED (IST/DEEC) 17 Árvores - Inserção (3) } while (child!= NULL){/* procura nó onde inserir dado */ upper = child; if (child->item == i) return tree; /* já existe! */ child = child->item>i? child->left : child->right;} /* criada e inicializada nova folha */ leaf = (node *) malloc(sizeof(node)); leaf->item = i; leaf->left = leaf->right = NULL; /* upper passa a nó intermédio e aponta para nova folha */ if (upper->item > i) upper->left = leaf; else upper->right = leaf; return tree; /* rotina insert retorna nova árvore, porque a inicial foi alterada */ AED (IST/DEEC) 18
Árvores - Inserção (4) Exemplo A5: inserção de 7 na árvore do exemplo A3 12 i) 7 < 12: insere na sub-árvore esquerda. ii) 7 > 5: insere na sub-árvore direita. iii) 8 é folha: cria nova folha. 7 < 8: a nova folha é sub-árvore esquerda. 2 5 7 i ii 8 iii 17 31 O algoritmo degenera numa lista, se os dados inseridos forem sucessivamente crescentes (ou sucessivamente decrescentes). AED (IST/DEEC) 19 Árvores - Inserção (5) A complexidade da inserção depende do tipo de árvore: Árvore degenerada (lista): O(N) Árvore perfeitamente balanceada: complexidade determinada pela pesquisa do local de inserção log 2 N, ou seja, O(log N) Árvore de inserção aleatória: considerando que a altura de cada subárvore possui uma distribuição uniforme 1..N, tem-se que a pesquisa do local de inserção custa ln N, ou seja, O(log N) AED (IST/DEEC) 20
Árvores - Inserção (6) Estratégia de prova: simplesmente, efectuar os cálculos! O custo é 1 (visita à raiz) somado ao custo da visita à sub-árvore: esta pode ter entre 0 e N-1 nós, com distribuição uniforme C N = 1 + 1/N * ΣC k-1 1 k N, para N 2 C 1 = 1 Para eliminar Σ, multiplica-se ambos os lados por N, e subtrai-se a fórmula para N-1 (nota: k varia até N nos dois Σ) NC N - (N-1)C N-1 = N + ΣC k-1 (N-1 + ΣC k-2 ) = 1 + C N-1 NC N = NC N-1 +1 C N = C N-1 + 1/N AED (IST/DEEC) 21 Árvores - Inserção (7) por substituição telescópica C = C N 1 + 1/N + 1/(N-1) +... + 1/2 Trata-se da série harmónica, pelo que C N ln N + γ + 1/12N QED Conclusão: o custo da inserção é mínimo se a árvore for balanceada O problema reside na maior dificuldade em manter a árvore balanceada (como se vai ver nos acetatos seguintes) AED (IST/DEEC) 22
Árvores balanceadas AVL (1) Def: Uma árvore diz-se balanceada AVL 2, sse em todos os nós a diferença entre as alturas das subárvores for igual ou inferior a 1. Exemplo A6: árvore A3 é balanceada, A4 já não é (subárvore esquerda tem altura 2 e direita tem altura 4). Para manter a árvore balanceada, respeitando a ordem, depois da inserção pode ser necessário rodar a configuração de nós (rotação simples e rotação dupla) 2 Adel son-vel skii e Landis AED (IST/DEEC) 23 Árvores balanceadas AVL (2) Operações de rotação são classificadas de acordo com o sentido (à direita e à esquerda). y Rotação à direita x α x β γ Rotação à esquerda Quando a diferença de alturas for igual a 2, a operação de rotação diminui o nível da folha mais afastada da raiz, mantendo a ordenação da árvore. α β y γ AED (IST/DEEC) 24
Árvores balanceadas AVL (3) A função de rotação apenas actualiza ponteiros (β é a única subárvore a mudar de ascendente). Assim, a rotação tem complexidade O(1). node *rotate_right(node * tree) { } node *x, *y, *beta; /* inalterada se árvore vazia, ou subárvore esquerda vazia */ if (tree == NULL) return tree; else if (tree->left == NULL) return tree; /* salva ponteiros */ y = tree; x = tree->left; beta = x->right; /* actualiza ligações */ x->right = y; y->left = beta; return x; AED (IST/DEEC) 25 Árvores balanceadas AVL (4) Há dois pares de casos em que se torna necessário rodar subárvores: 1: Rotação simples (à direita) x y h+1 A1 y h A2 A3 h h+1 A1 A2 x A3 h AED (IST/DEEC) 26
Árvores balanceadas AVL (5) 2: Rotação dupla (à direita) x z h A1 y z A4 h A1 y A2 A3 x A4 h A2 A3 A2 ou A3 de altura h AED (IST/DEEC) 27 Árvores balanceadas AVL (6) Código de inserção AVL int height(node *tree) { int hl, hr; if (tree == NULL) return 0; if (tree->left == NULL && tree->right == NULL) return 1; hl = height(tree->left); hr = height(tree->right); return hl>hr? 1+hl : 1+hr; } AED (IST/DEEC) 28
Árvores balanceadas AVL (7) node *insert(int i, node *tree) { /* tree é ponteiro para árvore: a alteração é feita directamente na árvore que é dada como parâmetro */ int h1, h2, h3; if (tree == NULL) { /* árvore vazia */ tree = (node*) malloc(sizeof(node)); } tree->item = i; tree->left = tree->right = NULL; return tree; AED (IST/DEEC) 29 Árvores balanceadas AVL (8) if (i == tree->item) return tree; /* já instalado */ if (i < tree->item){ /* insere na sub-árvore esquerda */ insert(i, tree->left); h1 = height(tree->left->left); h2 = height(tree->left->right); h3 = height(tree->right); if (h1 > h3) /* Caso 1, rotação simples à direita */ tree = rotate_right(tree); if (h2 > h3) { /* Caso 2, rotação dupla à direita */ tree->left = rotate_left(tree->left); tree = rotate_right(tree); } } AED (IST/DEEC) 30
Árvores balanceadas AVL (9) else {/* insere na sub-árvore direita */ insert(i, tree->right); h1 = height(tree->right->right); h2 = height(tree->right->left); h3 = height(tree->left); if (h1 > h3) /* Caso 1, rotação simples à esquerda */ tree = rotate_left(tree); if (h2 > h3) {/* Caso 2, rotação dupla à esquerda */ tree->right = rotate_right(tree->right); tree = rotate_left(tree); } } } retun tree; AED (IST/DEEC) 31 Árvores balanceadas AVL (10) O algoritmo AVL não garante distribuição total pelos níveis intermédios (por exemplo, na série S 0 +(-1) n n, a sub-árvore esquerda só deriva à esquerda e a sub-árvore direita só deriva à direita). O algoritmo de inserção listado é pesado devido à determinação das alturas. Uma versão mais eficiente (embora mais complexa e ocupando mais memória), guarda em cada nó o balanceamento das sub-árvores (maior à esquerda, iguais ou maior à direita). AED (IST/DEEC) 32
Síntese da Aula 2 de Árvores Inserção de elementos Árvores balanceadas AVL AED (IST/DEEC) 33 Árvores balanceadas RB (1) Def: Uma árvore balanceada RB 3 é uma BST, em que: Todos os nós possuem uma cor: Vermelha, Negra. A raiz e todas as folhas são negras. Filhos dos nós vermelhos são negros. Todos os caminhos, da raiz às folhas, possuem o mesmo número de nós negros (diferenças do número de nós vermelhos inferior a 2) Nota: nos grafos, ponteiros nulos considerados como folhas negras 3 Red Black AED (IST/DEEC) 34
Árvores balanceadas RB (2) Código de inserção RB 1 Inserir directamente, como nó vermelho. 2 Há 3 alterações possíveis. Se a alteração quebrar as regras das árvores RB, então rodar e recolorir em direcção à raiz. A inserção custa O(log N). AED (IST/DEEC) 35 Árvores balanceadas RB (3) A Se inserir como raiz, passar nó a negro. B Se o nó ascendente for negro, terminar. novo nó AED (IST/DEEC) 36
Árvores balanceadas RB (4) C Se o nó ascendente for vermelho, rodar duplamente e actualizar cores do pai/mãe, avô/avó e tio/tia x y novo nó y z w α β γ x w α y β x Pode ter de recolorir em cima z γ y z w x novo nó w α β γ α β z γ AED (IST/DEEC) 37 Árvores balanceadas RB (5) x w α β y w γ z y α β γ x z novo nó Codificação pode ser feita de 2 formas: Cada nó possuir ponteiro para o nó pai/mãe Recursivamente (muito complicado!) AED (IST/DEEC) 38
Árvores - Combinação (1) Combinação permite juntar, numa única árvore, duas árvores (cada uma com dados parciais) Se uma das árvores é nula, a combinação é apenas a outra árvore. Senão, insere-se os elementos da primeira na segunda. node *join(node *a, node *b) { if (a == NULL) return b; if (b == NULL) return a; b = insert(a->item, b); b = join(a->left, b); /* Sedgewick tem join(a->left,b->left); */ b = join(a->right, b);/* Sedgewick tem join(a->right,b->right); */ free(a); return b;} AED (IST/DEEC) 39 Árvores - Combinação (2) Solução proposta por Sedgewick (Prog 12.16) incorrecta! Fazer join, logo nas sub-árvores esquerda e direita, impede verificar o mesmo dado em nós de alturas distintas! 30 10 10-10 + 0 20-10 + 0 20 0 0 30 0 + -10 10 0 20 30-10 0 10 20 30 Errado!!! 0 AED (IST/DEEC) 40
Árvores - Retirada Um dado é eliminado da árvore, executando os passos Procurar o nó de residência Se existir, executar: Combinar sub-árvores esquerda e direita Substituir nó pelo resultado da combinação node *delete(int i, node *tree) { node *aux = tree; if (tree == NULL) return NULL; if(i < tree->item) tree->left = delete(i, tree->left); if(i > tree->item) tree->right = delete(i, tree->right); if(i == tree->item ) { tree = join(tree->left, tree->right); free(aux); } return tree;} AED (IST/DEEC) 41 Árvores - Varrimento (1) Existem diversas estratégias de percurso/varrimento (transverse) de árvores: 1 De acordo com o operador, existente em todos os nós que não sejam folhas (percurso também designado por dump) Pré-fixado: antes do varrimento das sub-árvores. In-fixado: varrer primeiro sub-árvore esquerda, imprimir operador e varrer depois a sub-árvore direita. Pós-fixado 4 : depois do varrimento das sub-árvores. 2 De acordo com o nível Profundidade (depth-first) Largura (breadth-first) 4 também conhecido por notação polaca invertida AED (IST/DEEC) 42
Árvores - Varrimento (2) 1: De acordo com o operador As subárvores são varridas de forma ordenada e o operador é listado de forma pré-determinada (pré-fixado, in-fixado ou pósfixado) Exemplo A7: percurso pós-fixado da árvore aritmética A2 typedef struct { enum {nod, leaf} node_type; union { enum {plus, times} oper; } data; int value; } info; AED (IST/DEEC) 43 Árvores - Varrimento (3) void postfix (node *tree){ if (tree == NULL) return; switch(tree->item.node_type) { case leaf: /* imprime o valor */ printf( %d, tree->item.info.value); break; case nod: /* imprime sub-árvores esquerda e direita */ postfix(tree->left); postfix(tree->right); switch(tree->item.info.oper) { /* imprime oper */ case plus: printf( + ); break; case times: printf( * ); break; }}} AED (IST/DEEC) 44
+ n 1 2 * n 2 n 3 3 5 n 4 n 5 Árvores - Varrimento (4) n 1 n 2 n 3 + 2 n 4 3 n 5 5 * o resultado final é: 2 3 5 * + AED (IST/DEEC) 45 Árvores - Varrimento (5) 2: De acordo com o nível Profundidade: idêntico ao varrimento pré-fixado void depth_first (node *tree){ if (tree ==NULL) return; printf( %d, tree->item); depth_first(tree->left); depth_first(tree->right); } Exemplo A8: O varrimento em profundidade da árvore A3 executa a seguinte sequência de passos: (a) elemento da raiz: 12 (b) visita em profundidade sub-árvore esquerda: 5 2 8 (c) visita em profundidade sub-árvore direita: 17 31. O resultado é 12 5 2 8 17 31 2 5 12 8 17 31 AED (IST/DEEC) 46
Árvores - Varrimento (6) Largura: O varrimento é feito por nível. As sub-árvores são colocadas no fim de uma lista. typedef struct _s2 { node *this; struct _s2 *next; } link; link *New(); /* cria lista vazia */ void InsertLast(link *, node *);/* insere no fim da lista */ link *GetFirst(link *);/* obtém primeiro elemento da lista */ AED (IST/DEEC) 47 Árvores - Varrimento (7) void breadht_first (link *list){ if (list == NULL) return; } else { link * aux = GetFirst(list); printf( %d, aux->this->item); InsertLast(list, aux->this->left); InsertLast(list, aux->this->right); breadht_first(list); } Rotina deve ser chamada com a raiz inserida na lista. breadht_first(insertlast(new(), root)); AED (IST/DEEC) 48
Árvores - Varrimento (8) Exemplo A9: varrimento em largura da árvore A3 2 5 12 8 17 31 L ista A cção S a ída [12] R ecolhe 1º elem e nto Im prim e valor 12 [5, 17] Inse re subárvores esq e dir [17] R ecolhe 1º elem e nto Im prim e valor 5 [17,2,8] Inse re subárvores esq e dir [2,8] R ecolhe 1º elem e nto Im prim e valor 17 [2,8,31] Inse re subárvores esq e dir [8,31] R ecolhe 1º elem e nto Im prim e valor 2 Inse re subárvores esq e dir [31] R ecolhe 1º elem e nto Im prim e valor 8 Inse re subárvores esq e dir [] R ecolhe 1º elem e nto Im prim e valor 31 Inse re subárvores esq e dir AED (IST/DEEC) 49 Síntese da Aula 3 de Árvores Árvores balanceadas RB Combinação de árvores Retirada de elementos Varrimento de árvores AED (IST/DEEC) 50