ALGORITMOS E
ESTRUTURA DE DADOS
Árvore Binária de Busca
Árvore Binária de Busca
Árvore Binária de Busca
Árvore Binária de Busca
Árvore Binária de Busca
Propriedade fundamental da árvore binária de busca
Valor da chave da raiz éMaior do que o valor da chave da subárvore da esquerda (SAE) Menor do que o valor da chave da subárvore da direita (SAD)
A subárvore da esquerda e subárvore da direita obedecem a
propriedade fundamental
Todos < x Todos ≥ x
Esquematicamente:
Exemplo:
Árvore Binária de Busca
Tipos de dados e funções e procedimentos já criados que podem
ser aproveitadas para a árvore binária de busca:
tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ;
funcao INICIALIZA:R; inicio
INICIALIZA = nil; fim;
funcao VAZIA(R: ARV):logico; inicio
se ARV = nil entao
VAZIA ←←←← verdadeiro; senao VAZIA ←←←← falso; fim-se; fim; Procedimentos de caminhamentos
Árvore Binária de Busca
As operações que são interessantes em analisar em detalhes são:
busca: função que busca um elemento na árvore;
insere: função que insere um novo elemento na árvore; retira: função que retira um elemento da árvore.
pois exploram a propriedade de ordenação das árvores binárias de busca.
Árvore Binária de Busca
Busca “simplificada“
Compara-se o valor com a raiz Se igual, achou
Se maior, então buscar na subárvore da direita (SAD) Se menor, então buscar na subárvore da esquerda (SAE)
Busca detalhada
Chamada para buscar um valor a partir do nó raiz
Comparação com valor do nó raiz. Se igual, achou:
Retornar verdadeiro
Se valor é menor que o valor do nó da raiz e
possui SAE:
chamada recursiva para nó raiz de SAE.
não possui SAE:
retornar falso
Se valor é maior que o valor do nó da raiz e
possui SAD:
chamada recursiva para nó raiz de SAD
não possui SAD:
Exemplo: Busca
2
nil 1 nil 5
nil 3 nil nil 6 nil
Qual o caminho percorrido para a chave 6?
Busca em uma árvore binária de busca
função BUSCA(RAIZ, CHAVE):log; tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ; tipot: CHAVE;
início
se RAIZ = nil então {elemento não encontrado} BUSCA ← falso;
senão se RAIZ↑.X = CHAVE entao
BUSCA ← verdadeiro; {elemento está na raiz} senão se CHAVE < RAIZ↑.X entao
{procura elemento na sub-árvore da esquerda} BUSCA ← BUSCA(RAIZ↑.ESQ, CHAVE);
senão se CHAVE > RAIZ↑.X entao
{procura elemento na sub-árvore da direita} BUSCA ← BUSCA(RAIZ↑.DIR, CHAVE);
fim-se; fim-se; fim-se; fim-se;
fim-função; {BUSCA}
Função recursiva que verifica se uma dada chave (valor a ser localizado) se encontra numa árvore de busca binária.
Exercício 3 - Busca
A partir da árvore binária a seguir, apresentar os caminhos
percorridos para encontrar os nós:
18 9 33 35 7 20 10 30 25 33 8 15 6 9 14 18
Complexidade: o número de comparações é proporcional a altura h da árvore: O(h). A altura da árvore é dada por log2N. Portanto, o número de operações é O(log2N), onde N é o número de nós da árvore.
Inserção
Inserir chave em novo nó no filho do nó folha mantendo a
propriedade da árvore binária de busca
r x T nil nil raiz Nó folha
Chave menor que chave do nó folha
Chave maior que chave do nó folha r x T nil r x T nil y y
Exemplo de inserção
Inserir chaves 15, 4, 20, 17, 19
15 15 4 15 4 20 15 4 20 17 15 4 20 17 19Inserção - Nó
procedimento INSERE(RAIZ, CHAVE); tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ; tipot: CHAVE;
se RAIZ = nil então {insere elemento como filho de uma folha} aloque(RAIZ); RAIZ↑.X ← CHAVE; RAIZ↑.ESQ ← nil; RAIZ↑.DIR ← nil; senao
se CHAVE < RAIZ↑.X entao
INSERE(RAIZ↑.ESQ, CHAVE); senao INSERE(RAIZ↑.DIR, CHAVE); fim-se; fim-se fim-procedimento; {INSERE}
Procedimento recursiva para inserir um nó na árvore binária de busca, cujo valor do nó a ser inserido é a variável CHAVE.
Exercício 4 - Inserção
Construir a árvore binária de busca correspondente à inserção
das seguintes chaves:
Remoção
Na remoção de um nó da árvore binária de busca, o nível de
complexidade depende da posição do nó a ser removido da
árvore, pois após a remoção a árvore deve preservar sua
propriedade fundamental.
Mais difícil remover nó que tem duas sub-árvores do que remover
nó folha.
Há três casos possíveis, o:
1) nó é uma folha (não tem filhos) 2) nó tem 1 filho 3) nó tem 2 filhos 15 15 4 15 4 20
Caso 1 Caso 2 15 Caso 3
Caso mais fácil de tratar.
O ponteiro apropriado de seu nó pai é ajustado para nil e o nó
é removido por desaloque (apagado da memória).
Exemplo: remover nó com conteúdo 19:
1) Remover nó folha
15 4 20 17 19 15 4 20 17 19 Libera espaço 1 1Procedimento para remover nó sem folha:
1) Remover nó folha
procedimento REMOVE_FOLHA(RAIZ) tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ;
se RAIZ↑.ESQ = nil e RAIZ↑.DIR = nil então
{remove um nó sem filho (folha)}
desaloque(RAIZ); RAIZ ←←←← nil;
senao
imprima(“Erro: nó tem filho(s)”);
fim-se
Libera espaço
2) Remover nó com 1 filho
Ponteiro do pai do nó a ser removido é reajustado para apontar
para o filho do nó a ser removido.
Ou seja, o pai vai apontar para o neto (que passa a ser filho).
Desse modo:
Filhos do nó são elevados em 1 nível
Bisnetos perdem um grau de descendência em suas designações de parentesco.
Exemplo: Remover nó 4
15 4 20 17 19 1 15 20 17 19 1 42) Remover nó com 1 filho
Procedimento para remover nó com um filho:
procedimento REMOVE_1FILHO(ref RAIZ) tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ; R: AUX;
se RAIZ↑.ESQ = nil então {só tem filho à direita}
AUX ←←←← RAIZ;
RAIZ ←←←← RAIZ↑↑↑↑.DIR;
desaloque(AUX);
senao se RAIZ↑.DIR = nil então
{só tem filho à esquerda}
AUX ←←←← RAIZ;
RAIZ ←←←← RAIZ↑↑↑.ESQ;↑
desaloque(AUX);
senao
imprima(“Erro: nó tem 2 filhos”);
fim-se; fim-se
3) Remover nó com 2 filhos
50 25 15 5 20 35 30 40 Deseja-se excluir o nó 25 50 15 5 20 35 30 40 Somente substituir com a raiz da sub-árvore resolve o problema?3) Remover nó com 2 filhos
Nó a ser removido tem dois filhos
Remover fazendo junção (merge). Não será visto. Remover fazendo cópia.
Remoção por cópia (Thomas Hibbard e Donald Knuth)
Substituir o nó pelo menor nó à direita e ajustar ponteiros
Busca do substituto:
ir para direita
procurar elemento mais à esquerda (menor filho à esquerda) guardar antecessor, para descobrir pai do substituto
Se a direita estiver nula, o substituto será o próximo à esquerda (Continua...)
3) Remover nó com 2 filhos
Ajuste dos ponteiros
ajustes no campo "esq" do pai do substituto e no campo "dir" do substituto (dispensáveis se nó à direita do removido é exatamente o substituto)
campo que referencia nó removido conterá substituto encontrado campo "esq" do substituto aponta para SAE do nó removido
Função que retorna uma ref para o menor elemento da
sub-árvore da direita da sub-árvore RAIZ:
funcao MENOR_SUBARV_DIREITA(RAIZ):R tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ; R: AUX;
se RAIZ↑.ESQ ≠ nil e RAIZ↑.DIR ≠ nil então
{Nó tem 2 filhos}
AUX ←←←← RAIZ↑↑↑↑.DIR;
enquanto(AUX↑↑↑↑.ESQ ≠≠≠≠ nil)
AUX ←←←← AUX↑↑↑.ESQ;↑
fim-enquanto;
MENOR_SUBARV_DIREITA ← AUX; senao
imprima(“Erro: nó RAIZ não tem 2 filhos”);
fim-se
fim-funcao; {MENOR_SUBARV_DIREITA}
3) Remover nó com 2 filhos
Acha o elemento mais à esquerda da sub-árvore à direita de RAIZ (menor)
AUX ou tem apenas um filho à direito ou não tem filho algum
Procedimento para remover nó com 2 filhos:
procedimento REMOVE_NO(RAIZ, V) tipo R = ref NO;
tipo NO = reg ( R : ESQ, tipot : X, R : DIR ); R : RAIZ; R: AUX; tipot: V;
se RAIZ = nil entao
imprima(“Árvore vazia ou ”,V,”não encontrado”); senao se V < RAIZ↑.X entao
REMOVE_NO(RAIZ↑↑↑.ESQ, V); {recursão:subarv. esquerda}↑ senao se V > RAIZ↑.X entao
REMOVE_NO(RAIZ↑↑↑.DIR, V); {recursão:subarv. direita}↑ senao {achou o nó a ser removido, RAIZ↑.X = V}
se RAIZ↑.ESQ = nil e RAIZ↑.DIR = nil então REMOVE_FOLHA(RAIZ);{RAIZ é uma folha}
senao se RAIZ↑.ESQ=nil ou RAIZ↑.DIR=nil então REMOVE_1FILHO(RAIZ); {RAIZ tem um filho} senao {RAIZ tem dois filhos}
AUX ←←←← MENOR_SUBARV_DIREITA(RAIZ);
RAIZ↑↑↑.X ←↑ ←←← AUX↑↑↑↑.X; {troca as informações} AUX↑↑↑.X ←↑ ←←← V;
REMOVE_NO(RAIZ↑↑↑↑.DIR, V);{recursão:subarv. direita} fim-se;
fim-se;
fim-procedimento; {REMOVE_NO}
3) Remover nó com 2 filhos
50 25 15 5 20 35 30 40 Deseja-se excluir o nó 25 Vamos para o filho direito do nó original Depois partimos sempre para a esquerda Sucessor!!! 50 15 5 20 30 40 35 Agora o problema está resolvido3) Remover nó com 2 filhos
E quando chegarmos ao filho direito do nó original e ele não
tiver filhos do lado esquerdo?
50 25 15 5 20 35 40 Deseja-se excluir o nó 25 Vamos para o filho direito do nó original Ele não possui filhos na esquerda! Sucessor!!! 50 15 5 20 35 40 Agora o problema está resolvido
14
Exemplo - Remoção
Remover 10
20 10 30 25 33 8 17 6 9 14 18 20 14 30 25 33 8 17 6 9 18 Nó 14 é o menor nó à direita do nó 10 enão tem filho à direita.
16
Remover 10
14Exemplo - Remoção
20 10 30 25 33 8 17 6 9 14 18 Nó 14 é o menor nó à direita do nó 10 etem filho à direita.
16 20 10 30 25 33 8 17 6 9 18 14 16
Exemplo - Remoção
Considere a seguinte árvore binária de busca
20
10 30
25 33 8 15
Exemplo - Remoção
Remover 14
20 14 30 25 33 8 15 6 9 18 20 15 30 25 33 8 18 6 9Exemplo - Remoção
Remover 15
20 18 30 25 33 8 6 9 20 15 30 25 33 8 18 6 9Exemplo - Remoção
Remover 18
20 30 25 33 8 6 9 20 18 30 25 33 8 6 9Exemplo - Remoção
Remover 6
20 30 25 33 8 9 20 30 25 33 8 6 9Exercício 5 – Remoção
A partir da árvore binária de busca construída no exercício de
inserção, contendo as chaves 40, 30, 15, 50, 45, 13, 80, 71, 20,
executar as operações. Apresente a árvore resultante após a
execução de cada operação.
Remover 50 Remover 30 Remover 13 Inserir 50 Remover 71
Exercícios
1. Fazer uma função para encontrar, e retornar, o maior elemento de uma árvore binária de busca.
2. Fazer uma função para encontrar, e retornar, o menor elemento de uma árvore binária de busca.
3. Fazer um procedimento para remover o maior elemento de uma árvore binária de busca.
4. Fazer um procedimento para remover o menor elemento de uma árvore binária de busca.
5. Alterar o(s) procedimento(s) de remoção de nó com dois filhos considerando, agora, o maior elemento da sub-árvore à esquerda como o elemento a ser “trocado” com o nó a ser removido.
Exercícios
6. Uma árvore binária de busca é considerada balanceada se sua altura h é próxima de log2(n). Fazer uma função para verificar se uma dada árvore binária de busca está balanceada. Considere uma