UFES - Curso de verão 2011 Estruturas de Dados I Profa. Juliana Pinheiro Campos jupcampos@gmail.com
Árvores binárias de busca (ou São árvores projetadas para dar suporte a operações de busca de forma eficiente. Árvores binárias de busca combinam as vantagens de 2 estruturas: Vetor ordenado (busca eficiente); Lista encadeada (inserir e remover elementos de forma eficiente). Armazena elementos que tem uma relação de ordem (>, <, =) entre si.
Árvores binárias de busca (ou As árvores binárias de busca possuem uma propriedade fundamental: O valor associado à raiz é sempre maior do que o valor associado a qualquer nó da subárvore à esquerda (sae) e é sempre menor do que o valor associado a qualquer nó da subárvore à direita (sad). Essa propriedade garante que, quando a árvore é percorrida em ordem simétrica (sae, raiz, sad), os valores são encontrados em ordem crescente.
Árvores binárias de busca (ou 6 2 8 Mostre a impressão dos valores da arvore caso fosse utilizada a ordem simétrica. 1 4 1 2 3 4 6 8 3
Árvores binárias de busca (ou Uma variação possível permite que haja repetição de valores na árvore: o valor associado à raiz é sempre maior que o valor associado a qualquer nó da sae, e é sempre menor ou igual ao valor associado a qualquer nó da sad.
Árvores binárias de busca (ou 6 1 2 8 4 Como a busca de um valor pode ser feito nessa árvore? Comparamos o valor buscado com a raiz. Se for igual, encontramos o valor buscado. Se for menor, a busca continua na sae; caso contrario, a busca continua na sad.. 3
Árvores binárias de busca (ou Procedimento Pesquisa para uma árvore binária de busca : Para encontrar um valor x, primeiro compareo com o valor que está na raiz. Se x é menor do que o valor que está na raiz, vá para a sae; se x é maior, vá para a sad. Repita o processo recursivamente, até que o valor procurado seja encontrado ou então um nó folha seja atingido.
Consideramos o caso em que a informação associada a um no é um número inteiro. struct arvore { int info; struct arvore* esq; struct arvore* dir; }; typedef struct arvore Arvore;
As funções que vamos analisar são: Busca: busca um elemento na árvore; Insere: insere um novo elemento na árvore na posição correta para que a propriedade fundamental seja mantida; Retira: retira um elemento da árvore mantendo a propriedade fundamental; As outras funções de árvores são análogas às funções vistas para árvores binárias comuns.
Busca: Arvore* busca(arvore* a, int v){ if(!estavazia(a)) { if (v < a->info) return busca(a->esq, v); else if(v > a->info) return busca(a->dir, v); else return a; } return NULL; }
Inserção: uma subárvore vazia atingida indica o ponto de inserção. Se a árvore for vazia, deve ser substituída por uma árvore cujo único nó contém o valor v; Se a árvore não for vazia, compara v com a raiz e inserimos v na sae ou na sad, conforme o resultado da comparação. É necessário atualizar os ponteiros para as subarvores à esquerda ou à direita, pois a função pode alterar o valor do ponteiro para a raiz da (sub) árvore.
Arvore* insere(arvore* a, int v){ if(a==null){ a = malloc(sizeof(arvore)); a->info = v; a->esq = a->dir = NULL; } else if (v < a->info) a->esq = insere(a->esq, v); else if(v > a->info) a->dir = insere(a->dir, v); } return a;
Remoção: Se a árvore for vazia, nada deve ser feito; Se a árvore não for vazia, compara v com a raiz e retiramos v na sae ou na sad, conforme o resultado da comparação. Ou retira elemento se for encontrado. Existem 3 situações possíveis: Retirar elemento que é folha Quando elemento possui um único filho Quando o elemento a ser retirado possui 2 filhos
ESTRUTURAS DE DADOS Remoção: Retirar elemento que é folha 6 2 8 Como fazer para retirar o nó 3? Basta retirar o elemento da árvore e atualizar o pai. 1 4 X3
Remoção: Quando elemento possui um único filho 6 1 2 8 X4 Como fazer para retirar o nó 4? É necessário acertar o ponteiro do pai, fazendo o único neto ser seu filho direto. Em seguida, retira-se o elemento da árvore. 3
Remoção: Quando o elemento a ser retirado possui 2 filhos. 6 2 8 Como fazer para retirar o nó 6? 1 4 3
Remoção: quando elemento possui 2 filhos Encontramos o elemento que precede o elemento a ser retirado na ordenação (o elemento mais à direita da subárvore a esquerda); Trocamos a informação com o nó encontrado; Retiramos da subárvore à esquerda (recursivamente) o nó encontrado (que agora contém o valor que se quer retirar). 6 2 8 4 2 8 4 2 8 1 4 1 6 1 6 X 3 3 3
Remoção: quando elemento possui 2 filhos O elemento pode ser substituído pelo elemento que o precede na ordenação (o elemento mais à direita da subárvore a esquerda); OU O elemento pode ser substituído pelo elemento que o sucede na ordenação (o elemento mais à esquerda da subárvore a direita);
Operações em árvores binárias de busca Arvore* retira(arvore* a, int v){ if(a==null){ return NULL; } else if (v < a->info) a->esq = retira(a->esq, v); else if(v > a->info) a->dir = retira(a->dir, v); else{ // Achou o elemento que quer remover if(a->esq == NULL && a->dir == NULL){ free(a); a = NULL; } else if(a->esq == NULL){ Arvore *t = a; a = a->dir; free(t); }...
Arvore* retira(arvore* a, int v){... else if(a->dir == NULL){ Arvore *t = a; a = a->esq; free(t); } else{ // Arvore tem os dois filhos Arvore *f = a->esq; while(f->dir!= NULL) f = f->dir; a->info = f->info; f->info = v; a->esq = retira(a->esq, v); } } return a; }
Análise de complexidade da operação de busca O número de comparações em função do número n de elementos na árvore em uma pesquisa com sucesso é: Melhor caso: Pior caso: Caso médio: C(n) = O(1) C(n) = O(n) C(n) = O(log n) Quando o elemento procurado se encontra na raiz. Quando os elementos estão em ordem crescente ou decrescente. Quando o número de elementos na sae e na sad é equilibrado. O tempo de execução dos algoritmos para árvores binárias de pesquisa dependem muito do formato das árvores.
É importante manter as árvores com altura pequena (próximo da altura mínima log n), isto é, manter as árvores com uma distribuição dos nós próxima à da árvore cheia. Escreva um programa que utilize as funções de árvore binária de busca estudadas.