Algorítmos e estrutura de dados III Carlos Oberdan Rolim Ciência da Computação Árvores de busca binária Problemas... Se um vetor tiver ordenado é possível obter um algoritmo de pesquisa muito eficiente denominado pesquisa binária Não é eficiente na inserção e remoção devido a necessidade de reorganização para manter a ordenação As listas ligadas resolvem esse problema com o uso de ponteiros Problema é que algoritmos de pesquisa são sequenciais Árvore binária resolve esse tipo de problema de forma eficiente Árvore de pesquisa binária Uma árvore de pesquisa binária é uma árvore binária armazenando chaves (ou itens) em seus nós internos e satisfazendo a seguinte propriedade: Seja u, v e w três nós tais que u é nó esquerdo de v e w é o nó direito. Temos key(u) key(v) key(w) Nós externos não armazenam itens (null) Itens da esqueda menor que a raiz Itens da direita maior ou igual a raiz Cada subarvore é uma arvore Uma travessia em ordem visita as chaves em ordem crescente 6 2 1 9 4 8 Árvore de pesquisa binária Árvore binária onde os elementos são organizados de forma que: x y<x z>x Árvore de pesquisa binária 2 9 1 3 3 7 1 5 8 2 5 6 7 Árvore de pesquisa binária Exemplo: 50, 20, 39, 8, 79, 26, 58, 15, 88, 4, 85, 96, 71, 42, 53. 50 20 79 8 4 39 15 26 58 42 53 88 71 85 96 Árvore de pesquisa binária Estrutura de dados dinâmica, com recuperação em tempo logarítmico. 1 2 3 4 Representação de um Nó struct arvore{ char info; struct arvore *esq, *dir; }; typedef struct arvore *arvorePTR; Uma árvore binária de busca vazia é representada por uma variável ponteiro com conteúdo nulo Árvore com ponteiro para raiz Inserindo um elemento em uma árvore de busca binária 5 14 (a) Árvore depois da inserção de 5 e 14 Inserindo um elemento em uma árvore de busca binária 5 14 8 (b) Árvore depois da inserção de 8 Inserindo um elemento em uma árvore de busca binária 5 2 14 8 (c) Árvore depois da inserção de 2 Inserindo um elemento em uma árvore de busca binária 5 2 14 8 (d) Árvore depois da inserção de 20 20 Inserindo um elemento em uma árvore de busca binária Exemplo: 50, 20, 39, 8, 79, 26, 58, 15, 88, 4, 85, 96, 71, 42, 53. 50 20 79 8 4 39 15 26 58 42 53 88 71 85 96 Inserindo um elemento em uma árvore de busca binária void tInsere (arvorePTR *plist, char x) { if((*plist) == NULL){ *plist=(arvorePTR) malloc (sizeof(struct arvore)); (*plist)->info=x; (*plist)->esq=NULL; (*plist)->dir=NULL; } else{ if (x<(*plist)->info){ tInsere (&((*plist)->esq),x); } else{ tInsere (&((*plist)->dir),x); } } Pesquisa de um elemento Se T é uma árvore nula, não há o que pesquisar; Se a raiz de T armazena o elemento x, a solução é imediata; Se x é menor que o valor da raiz de T, a busca prossegue na subarvore esquerda de T; Se x é maior ou igual a T, a busca prossegue na subarvore direita de T Pesquisa de um elemento AVRPTR aPesq(arvorePTR *plist, char x) { if (*plist == NULL) /*elemento não encontrado*/ return (NULL); else if (x==(*plist)->info) /*elemento encontrado na raiz*/ return (*plist); else if (x<(*plist)->info) /*procura-o numa subarvore*/ return(aPesq(&((*plist)->esq),x)); else return(aPesq(&((*plist)->dir),x)); } Remoção de um Elemento Três cenários: 1. Se a Raiz não possuí filhos, a solução é imediata. Podemos removê-la e anular T; 2. Se a raiz possuí um único filho, podemos remover o nó raiz e o nó filho toma o seu lugar; 3. Se a raiz possuir dois filhos, não é possível que ambos assumam o lugar do pai. Qual das subarvores deve assumir o lugar do nó pai removido? O que deve ser feito com a subarvore que não foi escolhida? Seu nó ficará órfão? Remoção de um Elemento Não pode produzir nós órfãos; Aplicação pode inserir nós com a mesma “info” (definição de árvore binária): Impossibilidade de existir em uma subarvore da esquerda elementos maiores ou iguais à raiz; Subarvore da direita não pode existir nenhum elemento menor que a raiz; Assim: Se identificarmos o maior elemento da subarvore da esquerda e o posicionarmos na raiz, a árvore obtida continua seguindo a definição de árvore binária; O maior elemento em uma árvore binária ordenada encontra-se no nó que ocupa a posição mais à direita; com certeza esse nó não possuirá um filho à direita; Mas poderá possuir um filho a esquerda …. Remoção de um Elemento Detalhando uma solução: Elaborar uma função auxiliar; recebe um ponteiro para uma árvore não nula; Faz uma pesquisa para identificar o nó que armazena o seu maior elemento; Retorna o endereço deste nó; ARVPTR tMaior(TREEPTR *plist){ TREEPTR t; t=*plist; if (t->dir == NULL){ *plist = (*plist)->esq; return (t); }else return(tMaior(&((*plist)->dir))); } Remoção de um Elemento Possíveis passos para remover um nó que tenha dois filhos: p=tMaior(&(t->esq)); /*desliga o nó com o maior valor */ t->info = p->info; free(p); /*armazena o valor na raiz da árvore */ /*libera o nó removido */