EAD ARVORE BINÁRIA - ARMAZENAMENTO NÃO SEQUENCIAL COM VETORES Árvores Binárias de Pesquisa (ABP) - Definição Árvore binária em que os elementos dos seus nodos têm associado uma chave, que - determina a sua posição de inserção na árvore e - obedece às seguintes regras: a chave do elemento de um nodo é - maior do que a chave do elemento de qualquer nodo da sua subárvore esquerda e - menor do que a chave do elemento de qualquer nodo da sua subárvore direita; - ou seja, a árvore está em ordem crescente. Cada nodo pode ser visto como a raiz de duas árvores: - a árvore esquerda com nodos cujos elementos têm chaves inferiores à chave da raiz, e - a árvore direita com nodos cujos elementos têm chaves superiores à chave da raiz. O objetivo de organizar dados em ABP é facilitar a procura de um elemento: - a partir da raiz e da chave a ser encontrada, é possível saber qual o caminho a ser percorrido até encontrar o elemento/nodo com aquela chave; - basta verificar se a chave do elemento procurado é maior ou menor à chave do elemento na posição de pesquisa atual. 1
Árvores Binárias de Pesquisa (ABP) - Exemplos 2
Exemplo: Árvore com 10 nodos, em que o elemento de cada nodo é um inteiro. Representação gráfica: Estrutura de dados: Arvore(k) = struct { Elemento, Esquerda, Direita }, k = 1,..., 10 Arvore é um tipo de dados estruturado, mais especificamente um registo (estrutura); Elemento é um tipo de dados não estruturado básico (inteiro). Raiz = 3 3
Arvore 1 2 3 4 5 6 7 8 9 10 Elemento 86 28 59 81 49 47 71 76 11 40 Esquerda 0 9 2 0 0 10 0 7 0 0 Direita 0 6 8 1 0 5 0 4 0 0 Implementação em MatLab: Arvore(1).Elemento = 86; Arvore(1).Esquerda = 0; Arvore(1).Direita = 0; Arvore(2).Elemento = 28; Arvore(2).Esquerda = 9; Arvore(2).Direita = 6; Arvore(3).Elemento = 59; Arvore(3).Esquerda = 2; Arvore(3).Direita = 8; Arvore(4).Elemento = 81; Arvore(4).Esquerda = 0; Arvore(4).Direita = 1; Arvore(5).Elemento = 49; Arvore(5).Esquerda = 0; Arvore(5).Direita = 0; Arvore(6).Elemento = 47; Arvore(6).Esquerda = 10; Arvore(6).Direita = 5; Arvore(7).Elemento = 71; Arvore(7).Esquerda = 0; Arvore(7).Direita = 0; Arvore(8).Elemento = 76; Arvore(8).Esquerda = 7; Arvore(8).Direita = 4; Arvore(9).Elemento = 40; Arvore(9).Esquerda = 0; Arvore(9).Direita = 0; Arvore(10).Elemento = 11; Arvore(10).Esquerda = 0; Arvore(10).Direita = 0; 4
Operações sobre uma árvore binária de pesquisa: - As operações que incidem sobre uma árvore binária genérica podem também ser aplicada a uma árvore binária de pesquisa; - No entanto, algumas destas operações terão que sofrer alterações de forma a obedecer à definição de "Árvore Binária de Pesquisa", tais como: - Pesquisar um elemento numa árvore binária de pesquisa - Consultar um elemento numa árvore binária de pesquisa - Alterar/atualizar os dados de um Elemento de uma árvore binária de pesquisa - Determinar o maior elemento de uma árvore binária de pesquisa - Determinar o menor elemento de uma árvore binária de pesquisa - Inserir um elemento numa árvore binária de pesquisa - Remover um elemento de uma árvore binária de pesquisa 5
Pesquisar um elemento numa Árvore Binária de Pesquisa Parâmetros: Entrada: elemento a pesquisar (Elem), árvore (Arvore) e raiz da árvore (Raiz) Saída: índice do nodo com o elemento a pesquisar (indnodo) ou 0 (se não existe) Função implementada em MatLab (recursiva): Nota: CompararElementos é uma operação específica do problema function [indnodo] = PesquisarElementoABP (Elem, Arvore, Raiz) if Raiz == 0 indnodo = 0; return; comp = CompararElementos(Elem, Arvore(Raiz).Elemento); if comp == 0 indnodo = Raiz; % Elem = elemento da Raiz elseif comp > 0 % Elem > elemento da Raiz, pesquisar o elemento na subárvore direita indnodo = PesquisarElementoABP(Elem, Arvore, Arvore(Raiz).Direita); else % Elem < elemento da Raiz, pesquisar o elemento na subárvore esquerda indnodo = PesquisarElementoABP(Elem, Arvore, Arvore(Raiz).Esquerda); 6
Consultar um elemento numa Árvore Binária de Pesquisa Parâmetros: Entrada: elemento a consultar (Elem), árvore (Arvore) e raiz da árvore (Raiz) Saída: mostra os dados do elemento com a mesma Chave de Elem (se existe na árvore) Função implementada em MatLab (iterativa): Nota: MostrarElemento é uma operação específica do problema function [] = ConsultarElementoABP (Elem, Arvore, Raiz) % indnodo é o índice do nodo com Elem (se indnodo = 0, então Elem não existe) indnodo = PesquisarElementoABP(Elem, Arvore, Raiz); if indnodo == 0 % Elem não pertence à lista disp('elemento nao pertence a lista!'); else % Elem pertence à lista MostrarElemento(Arvore(indNodo).Elemento); 7
Alterar/atualizar um elemento de uma árvore binária de pesquisa Notas: - a informação contida no elemento fornecido para atualizar basta incidir sobre um dos seus campos, normalmente o que serve de Chave (valor que caracteriza o elemento); - a informação alterada não pode incidir sobre a Chave do elemento, pois é valor deste campo de define a posição do elemento numa árvore binária de pesquisa. Parâmetros: Entrada: elemento a alterar (Elem), árvore (Arvore) e raiz da árvore (Raiz) Saída: árvore atualizada com a nova informação sobre o Elem (Arvore) Função implementada em MatLab (recursiva): Nota: AlterarElemento é uma operação específica do problema que, sendo uma ABP não pode alterar a Chave do elemento, pois é o valor deste campo que indica a posição do elemento na árvore function [Arvore] = AlterarElementoABP (Elem, Arvore, Raiz) indnodo = PesquisarElementoABP(Elem, Arvore, Raiz); if indnodo ~= 0 Arvore(indNodo).Elemento = AlterarElemento(Arvore(indNodo).Elemento); 8
Determinar o maior elemento de uma Árvore Binária de Pesquisa Notas: - o maior elemento encontra-se no nodo mais à direita da árvore; - a árvore não pode ser vazia. Parâmetros: Entrada: árvore (Arvore) e raiz da árvore (Raiz) Saída: o maior elemento da árvore (maiorelem) Função implementada em MatLab (recursiva): function [maiorelem] = DeterminarMaiorElementoABP (Arvore, Raiz) if Arvore(Raiz).Direita == 0 % Caso terminal/paragem maiorelem = Arvore(Raiz).Elemento; else % Caso geral maiorelem = DeterminarMaiorElementoABP(Arvore, Arvore(Raiz).Direita); 9
Determinar o menor elemento de uma Árvore Binária de Pesquisa Notas: - o menor elemento encontra-se no nodo mais à esquerda da árvore; - a árvore não pode ser vazia. Parâmetros: Entrada: árvore (Arvore) e raiz da árvore (Raiz) Saída: o menor elemento da árvore (menorelem) Função implementada em MatLab (recursiva): function [menorelem] = DeterminarMenorElementoABP (Arvore, Raiz) if Arvore(Raiz).Esquerda == 0 % Caso terminal/paragem menorelem = Arvore(Raiz).Elemento; else % Caso geral menorelem = DeterminarMenorElementoABP(Arvore, Arvore(Raiz).Esquerda); 10
Inserir um elemento numa Árvore Binária de Pesquisa Notas: - Este processo tem que obedecer à definição de ABP: - a chave do elemento a inserir não pode ainda existir na árvore, e - a sua posição de inserção tem que assegurar que a chave de um elemento é - maior do que as chaves de todos os elementos da sua subárvore esquerda e - menor do que as chaves de todos os elementos da sua subárvores direita. - O nodo a inserir com o novo elemento é sempre inserido como folha da árvore. - O elemento é sempre inserido, mesmo que já exista na árvore. Parâmetros: Entrada: elemento a inserir (Elem), árvore (Arvore) e raiz da árvore (Raiz) Saída: árvore (Arvore) e raiz da árvore (Raiz) atualizadas 11
Função implementada em MatLab (iteratoiva): Nota: CompararElementos é uma operação específica do problema function [Arvore, Raiz] = InserirElementoABP (Elem, Arvore, Raiz) Nodo = CriarNodoArvore(Elem); if Raiz == 0 Arvore(1) = Nodo; Raiz = 1; return; indnodo = NumeroNodosArvore(Arvore, Raiz) + 1; painodo = DeterminarPaiNovoNodo(Elem, Arvore, Raiz, 0); Arvore(indNodo) = Nodo; if CompararElementos(Elem, Arvore(paiNodo).Elemento) < 0 Arvore(paiNodo).Esquerda = indnodo; else Arvore(paiNodo).Direita = indnodo; 12
function [painodo] = DeterminarPaiNovoNodo (Elem, Arvore, Raiz, pairaiz) if Raiz == 0 painodo = pairaiz; return; comp = CompararElementos(Elem, Arvore(Raiz).Elemento); if comp < 0 painodo = DeterminarPaiNovoNodo(Elem, Arvore, Arvore(Raiz).Esquerda, Raiz); else painodo = DeterminarPaiNovoNodo(Elem, Arvore, Arvore(Raiz).Direita, Raiz); 13
Remover um elemento numa Árvore Binária de Pesquisa Notas: - Deve-se considerar três casos distintos, segundo as características do nodo a remover: - se é uma folha, - se é um nodo com um único filho ou - se é um nodo com dois filhos. - Os mecanismos para tratar os dois primeiros casos são os mesmos descritos para o caso de árvores binárias genéricas. - O mecanismo para tratar o terceiro caso, remoção de um nodo com dois filhos, tem em conta as características de uma árvore binária de pesquisa (os seus elementos estão organizados segundo um critério). 14
Remoção de um nodo com dois filhos - O mecanismo de remoção implica substituir este nodo pelo nodo com chave mais próxima (maior ou menor) da chave do elemento a ser removido. - Esta operação poderá realiza-se de duas formas diferentes: - substituir a informação associada ao nodo a ser removido pela informação do seu "sucessor", que é o nodo mais à esquerda da subárvore direita (é o menor elemento da subárvore direita, que é maior do que qualquer elemento da subárvore esquerda); - substituir a informação associada ao nodo a ser removido pelo seu "antecessor", que é o nodo mais à direita da subárvore esquerda (é o maior elemento da subárvore esquerda, que é menor do que qualquer elemento da subárvores direita). - A escolha da forma a usar (substituir pelo "sucessor" ou "antecessor") deverá ter em consideração a altura das respetivas subárvores (usar o nodo da subárvore com maior altura). 15
Parâmetros: Entrada: elemento a remover (Elem), árvore (Arvore) e raiz da árvore (Raiz) Saída: árvore (Arvore) e raiz da árvore (Raiz) atualizadas Algoritmo (iterativo): Pesquisar o nodo com o elemento a remover (Nodo) Se o elemento a remover não existe na árvore (Nodo = 0) Então SAIR Senão Analisar os 4 casos possíveis seguintes 16
1º caso: o Nodo tem 2 filhos (subárvores esquerda e direita não vazias) Pesquisar o nodo com o elemento para substituir o elemento de Nodo (NodoSub) Determinar a altura da subárvore esquerda de Nodo (AlturaEsq) Determinar a altura da subárvore direita de Nodo (AlturaDir) Se AlturaEsq > AlturaDir Então NodoSub é o nodo com o maior elemento da subárvore esquerda de Nodo Senão NodoSub é o nodo com o menor elemento da subárvore direita de Nodo Pesquisar o pai de NodoSub (PaiNodoSub) Substituir o elemento de Nodo pelo elemento de NodoSub Remover o NodoSub da árvore, ligando PaiNodoSub a um dos filhos de NodoSub Se NodoSub é filho esquerdo de PaiNodoSub Então O filho esquerdo de NodoSub passa a ser filho esquerdo de PaiNodoSub Senão O filho direito de NodoSub passa a ser filho direito de PaiNodoSub Libertar o NodoSub 17
2º caso: o Nodo é uma folha (subárvores esquerda e direita vazias) Se o Nodo é a raiz da árvore (logo, a árvore só tem um elemento) Então Remover o Nodo da árvore (tornar a árvore vazia, Raiz = 0) Senão Pesquisar o pai de Nodo (PaiNodo) Remover o Nodo da árvore Se o Nodo é folha esquerda de PaiNodo Então Tornar a subárvore esquerda de Nodo vazia Senão Tornar a subárvore direita de Nodo vazia Libertar o Nodo 18
3º caso: o Nodo tem apenas tem um filho esquerdo (não tem filho direito) Se o Nodo é a raiz da árvore Então Atualizar a raiz da árvore (passando a ser o seu filho esquerdo) Senão Pesquisar o pai de Nodo (PaiNodo) Remover o Nodo da árvore Se o Nodo é folha esquerda de PaiNodo Então Tornar o filho (esquerdo) de Nodo o filho esquerdo de PaiNodo Senão Tornar o filho (esquerdo) de Nodo o filho direito de PaiNodo Libertar o Nodo 19
4º caso: o Nodo tem apenas tem um filho direito (não tem filho esquerdo) Se o Nodo é a raiz da árvore Então Atualizar a raiz da árvore (passando a ser o seu filho direito) Senão Pesquisar o pai de Nodo (PaiNodo) Remover o Nodo da árvore Se o Nodo é folha esquerda de PaiNodo Então Tornar o filho (direito) de Nodo o filho esquerda de PaiNodo Senão Tornar o filho (direito) de Nodo o filho direito de PaiNodo Libertar o Nodo Função implementada em MatLab (iterativa): function [Arvore, Raiz] = RemoverElementoABP (Elem, Arvore, Raiz) % pesquisar o nodo com o elemento a remover indnodo = PesquisarElementoABP(Elem, Arvore, Raiz); if indnodo == 0 % se o elemento não existe na árvore return; 20
% 1º caso: nodo a remover tem 2 filhos if Arvore(indNodo).Esquerda ~= 0 & Arvore(indNodo).Direita ~= 0 altesq = AlturaArvore(Arvore, Arvore(indNodo).Esquerda); altdir = AlturaArvore(Arvore, Arvore(indNodo).Direita); if altesq > altdir indsub = IndiceMaiorElementoABP(Arvore, Arvore(indNodo).Esquerda, indnodo); else indsub = IndiceMenorElementoABP(Arvore, Arvore(indNodo).Direita, indnodo); indpaisub = PesquisarNodoPai(Arvore(indSub).Elemento, Arvore, indnodo, indnodo); Arvore(indNodo).Elemento = Arvore(indSub).Elemento; if Arvore(indPaiSub).Esquerda == indsub Arvore(indPaiSub).Esquerda = Arvore(indSub).Esquerda; else Arvore(indPaiSub).Direita = Arvore(indSub).Direita; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indsub); return; 21
% 2º caso: nodo a remover é uma folha if Arvore(indNodo).Esquerda == 0 & Arvore(indNodo).Direita == 0 if indnodo == Raiz % o elemento a remover está na raiz da árvore; portanto, a árvore fica vazia Raiz = 0; return; painodo = PesquisarNodoPai(Elem, Arvore, Raiz, 0); if Arvore(paiNodo).Esquerda == indnodo Arvore(paiNodo).Esquerda = 0; else Arvore(paiNodo).Direita = 0; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indnodo); return; 22
% 3º caso: o nodo a remover apenas tem filho esquerdo if Arvore(indNodo).Esquerda ~= 0 if indnodo == Raiz % o elemento a remover está na raiz da árvore Raiz = Arvore(Raiz).Esquerda; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indnodo); return; painodo = PesquisarNodoPai(Elem, Arvore, Raiz, 0); if Arvore(paiNodo).Esquerda == indnodo Arvore(paiNodo).Esquerda = Arvore(indNodo).Esquerda; else Arvore(paiNodo).Direita = Arvore(indNodo).Esquerda; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indnodo); return; 23
% 4º caso: o nodo a remover apenas tem filho direito if painodo == 0 Raiz = Arvore(Raiz).Direita; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indnodo); return; painodo = PesquisarNodoPai(Elem, Arvore, Raiz, 0); if Arvore(paiNodo).Esquerda == indnodo Arvore(paiNodo).Esquerda = Arvore(indNodo).Direita; else Arvore(paiNodo).Direita = Arvore(indNodo).Direita; [Arvore, Raiz] = LibertarNodoArvore(Arvore, Raiz, indnodo); 24
Operações auxiliares da operação remover elemento de uma ABP Operação: Determinar o índice do nodo pai do nodo que contem um dado elemento Parâmetros: Entrada: elemento (Elem), árvore (Arvore), raiz da árvore (Raiz) e pai da raiz (pairaiz) Saída: índice do nodo pai do nodo com Elem (painodo) Função implementada em MatLab (recursiva): Nota: CompararElementos é uma operação específica do problema function [painodo] = PesquisarNodoPai (Elem, Arvore, Raiz, pairaiz) comp = CompararElementos(Arvore(Raiz).Elemento, Elem); if comp == 0 painodo = pairaiz; elseif comp > 0 painodo = PesquisarNodoPai(Elem, Arvore, Arvore(Raiz).Esquerda, Raiz); else painodo = PesquisarNodoPai(Elem, Arvore, Arvore(Raiz).Direita, Raiz); 25
Operação: Determinar o índice do maior elemento (o que se encontra no nodo mais à direita) Parâmetros: Entrada: árvore (Arvore), raiz da árvore (Raiz) e pai da raiz (pairaiz) Saída: índice do nodo que contém o maior elemento da árvore (indmaior) Função implementada em MatLab (recursiva): function [indmaior] = IndiceMaiorElementoABP (Arvore, Raiz, pairaiz) if Arvore(Raiz).Direita == 0 indmaior = Raiz; else indmaior = IndiceMaiorElementoABP(Arvore, Arvore(Raiz).Direita, Raiz); 26
Operação: Determinar o índice do menor elemento (o que se encontra no nodo mais à esquerda) Parâmetros: Entrada: árvore (Arvore), raiz da árvore (Raiz) e pai da raiz (pairaiz) Saída: índice do nodo que contém o maior elemento da árvore (indmenor) Função implementada em MatLab (recursiva): function [indmenor] = IndiceMenorElementoABP (Arvore, Raiz, pairaiz) if Arvore(Raiz).Esquerda == 0 indmenor = Raiz; else indmenor = IndiceMenorElementoABP(Arvore, Arvore(Raiz).Esquerda, Raiz); 27