Árvores de Busca
Igor Machado Coelho
01/06/2025
São requisitos para essa aula:
Agradecimentos especiais ao prof. Fabiano Oliveira e prof. Fábio Protti, cujo conteúdo didático forma a base desses slides
Consideramos o Problema da Busca em que, dados:
Responda: x pertence a S?
Em caso positivo, encontrar si tal que si=x.
Desafio: Como organizar os dados de forma a facilitar a operação de busca?
Podemos utilizar uma Árvore Binária rotulada T, tal que:
T é uma Árvore Binária de Busca (ABB)
Exemplos de ABB, contendo nós D, E, F, L, M, N e O.
Relembrando (aula de Árvores) a estrutura de árvore binária considerada:
Podemos resolver o Problema da Busca, com chave de busca c, através de uma ABB.
Ideia Geral:
v->chave == cc < v->chave: refaça o algoritmo na subárvore
esquerdac > v->chave: refaça o algoritmo na subárvore
direitaAvalie se as árvores abaixo são árvores binárias de busca:
Solução: nenhuma delas é! Erros: B<A (na A1); H>K (na A4).
buscaABBImplementação da busca em árvores binárias de busca:
auto* buscaABB(auto* no, char c) {
if(!no) return no; // chave não encontrada
if(no->chave == c) return no; // chave encontrada
if(c < no->chave)
return buscaABB(no->esq, c); // recursão esquerda
else
return buscaABB(no->dir, c); // recursão direita
}Pergunta: Quantos chamadas recursivas esse algoritmo pode precisar?
Resposta: Em uma árvore degenerada com N nós, até N passos (observe que, nesse caso, N também é a altura da árvore)
Encontre o pior caso (pior chave de busca) para a
execução do algoritmo buscaABB nas quatro árvores abaixo
(avalie primeiro se são ou não árvores binárias de busca):
Solução: 1. N/A, 2. N/A, 3. D, 4. N/A, Fig.7 L
Como a buscaABB depende a altura da árvore, qual o
melhor caso possível para a busca (menor altura possível) em uma árvore
binária com
N
nós?
Relembrando: uma árvore binária completa (ou cheia/perfeita) possui ⌈log2(N+1)⌉ níveis.
Solução: 1. N/A, 2. N/A, 3. Falso, 4. N=7 e log28=3 (Fig.7 tem log29=4)
Dado um nó do tipo NoEnc5, calcule o menor elemento da
árvore, com método:
NoEnc5* minimo(NoEnc5* const no) { ... }
Dado um nó do tipo NoEnc5, calcule o elemento sucessor
na árvore, com método:
NoEnc5* sucessor(NoEnc5* const no) { ... }
. . .
Dado um nó do tipo NoEnc6, com chave char e dado
complementar float, implemente a operação upsert: um
update, caso chave exista, ou um insert, caso chave
não exista:
void upsertABB(char c, float v, NoEnc6* no) { ... }
. . .
void upsertABB(char c, float v, NoEnc6* no) { // pre(no)
if (c == no->chave) { no->dado = v; return; }
if (c < no->chave) {
if (no->esq) upsertABB(c, v, no->esq);
else no->esq =
new NoEnc6{.chave=c,.dado=v,.esq=0,.dir=0,.pai=no};
} else {
if (no->dir) upsertABB(c, v, no->dir);
else no->dir =
new NoEnc6{.chave=c,.dado=v,.esq=0,.dir=0,.pai=no};
}
}Relembrando (aula de Árvores) a estrutura de árvore binária com pai considerada:
insereABBImplementação de inserção em árvore binária de busca não-vazia:
void insereABB(char c, NoEnc5* no)
// pre(no) // C++26
{
if(c <= no->chave) {
if(no->esq) insereABB(c, no->esq);
else no->esq = new NoEnc5{.chave=c, .esq=0, .dir=0, .pai=no};
}
else {
if(no->dir) insereABB(c, no->dir);
else no->dir = new NoEnc5{.chave=c, .esq=0, .dir=0, .pai=no};
}
}Pergunta: Quantos chamadas recursivas esse algoritmo pode precisar? R: O(N).
removeABBImplementação de remoção em árvore binária de busca (retorna nova raiz):
auto removeABB(char c, auto* raiz) {
if(!raiz) return std::tuple{false, raiz};
auto* no = ::buscaABB(raiz, c); if(!no) return std::tuple{false, raiz};
if(tem_dois_filhos(no)) {
auto* removido = sucessor(no);
extrai(removido); // 'removido' não tem filho esquerdo
no->chave = removido->chave;
delete removido;
return std::tuple{true, raiz};
} else {
auto [pai, filho] = extrai(no); // 'no' tem no máximo um filho
delete no;
if (!pai) return std::tuple{true, filho};
return std::tuple{true, raiz};
}
} // Exercicio: entenda cada caso e cada tipo de retorno!Finalmente, temos uma ABB completa:
export struct ABB {
NoEnc5* raiz; // raiz da árvore
int N;
void cria() { N = 0; raiz = 0; }
void libera() { if(raiz) ::destroi_bin(raiz); raiz = 0; }
NoEnc5* busca(char c) { return ::buscaABB(raiz, c); }
void insere(char c) {
if(!raiz) raiz = new NoEnc5{.chave = c, .esq=0, .dir=0, .pai=0};
else ::insereABB(c, raiz);
N++;
}
bool remove(char c) {
auto [b, nraiz] = ::removeABB(c, raiz); raiz = nraiz; if(b) N--;
return b;
}
}; // fimConclusão:
Árvores Binárias de Busca (ABB) são muito úteis para busca e organização da informação.
Porém, elas podem se degenerar! Para isso precisaremos estudar as Árvores Balanceadas.
Além da bibliografia do curso, recomendamos para esse tópico:
Em especial, agradeço aos colegas que elaboraram bons materiais, como o prof. Fabiano Oliveira (IME-UERJ), e o prof. Jayme Szwarcfiter cujos conceitos formam o cerne desses slides.
Estendo os agradecimentos aos demais colegas que colaboraram com a elaboração do material do curso de Pesquisa Operacional, que abriu caminho para verificação prática dessa tecnologia de slides.
Esse material de curso só é possível graças aos inúmeros projetos de código-aberto que são necessários a ele, incluindo:
Agradecimento especial a empresas que suportam projetos livres envolvidos nesse curso:
Esses slides foram escritos utilizando pandoc, segundo o tutorial ilectures:
Exceto expressamente mencionado (com as devidas ressalvas ao material cedido por colegas), a licença será Creative Commons.
Licença: CC-BY 4.0 2020
Igor Machado Coelho