Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

ESTRUTURA DE 
DADOS EM JAVA 
OBJETIVOS DE APRENDIZAGEM
 > Definir árvore binária, suas propriedades e como percorrê-la.
 > Reconhecer as operações básicas de árvores binárias.
 > Exemplificar a utilização de árvores binárias em Java.
Introdução
Árvores estão entre as estruturas de dados mais importantes da ciência da com-
putação, pois são fáceis de implementar e possuem um grande poder expressivo: 
são ideais para representar relações de hierarquia, subordinação, composição, 
organização e ordenação entre objetos. Além disso, árvores são estruturas de 
dados mais eficientes do que listas, principalmente quando comparamos as 
operações de inserção e consulta aos dados.
Neste capítulo, apresentaremos as árvores binárias. Inicialmente, conhecere-
mos a terminologia básica desse tipo de estrutura de dados e suas propriedades 
principais. Na sequência, estudaremos alguns algoritmos de manipulação desse 
tipo de árvore e entenderemos seu funcionamento. Finalmente, implementare-
mos esse tipo abstrato de dados na linguagem Java, utilizando o paradigma de 
programação orientada a objetos.
Árvores binárias 
em Java
Lucas Rafael Costella Pessutto
Árvores binárias
Árvores são estruturas de dados não lineares, o que signifi ca que cada nodo de 
uma árvore possui apontadores para dois (ou mais) nodos. Diferentemente de 
listas, que são estruturas lineares, não há a noção de antes e depois entre os 
elementos de uma árvore, mas uma relação hierárquica entre seus nodos, que 
se encontram em diferentes níveis nessa hierarquia (GOODRICH; TAMASSIA, 2013). 
Um exemplo clássico de árvore é a organização dos diretórios e arquivos 
em um sistema operacional, como mostrado na Figura 1. A origem do sistema 
de arquivos é o diretório-raiz e, a partir dele, ramificam-se todos os demais 
diretórios; ou seja, a partir da raiz, podemos alcançar qualquer arquivo pre-
sente nesse sistema operacional apenas descendo na hierarquia de diretórios. 
Figura 1. Árvore de arquivos de um sistema operacional.
Fonte: Tanenbaum e Woodhull (2008, p. 455).
Há diversos outros exemplos de árvores em nosso cotidiano, como uma 
árvore genealógica, um organograma de uma empresa e a organização de um 
livro, com seu título no nível hierárquico superior, seus capítulos no segundo 
nível e as seções em um nível abaixo deste, ligadas a seu respectivo capítulo.
Nomenclatura e propriedades
Árvores possuem uma nomenclatura especial, com inspiração biológica. 
Portanto, costumamos nos referir aos nodos como “raiz” e “folhas”. Também 
é comum utilizar as relações parentais para categorizar as posições relativas 
entre dois nodos da árvore: pai/mãe e fi lho, nodos irmãos, avô e neto, etc.
Árvores binárias em Java2
O primeiro nodo de uma árvore é chamado de raiz. Como mencionado 
anteriormente, todo nodo de uma árvore aponta para dois ou mais nodos, 
chamados de nodos filhos, e cada nodo filho tem um único pai (DEITEL; DEITEL, 
2017). Discutiremos, neste capítulo, somente as árvores binárias, que são 
aquelas cujo cada nodo possui no máximo dois filhos, um à esquerda e outro 
à direita. A Figura 2 contém um exemplo de árvore binária. Sua raiz é o nodo 
G e seus filhos são os nodos D (à esquerda) e J (à direita). Como os nodos D 
e J estão no mesmo nível da árvore, dizemos que esses nodos são irmãos. 
Outra forma de se referir a esses nodos é como raízes das subárvores (direita 
e esquerda) de G. Quando um nodo não possui filho, o chamamos de folha. 
Há cinco nodos folha na árvore da Figura 2: A, C, F, I e K.
Figura 2. Exemplo de árvore binária.
A altura de uma árvore consiste na quantidade de níveis em que a árvore 
foi construída. A altura da árvore da Figura 2 é quatro, pois ela contém quatro 
níveis: o primeiro, com a raiz (G), o segundo, com os filhos da raiz (D e J), o 
terceiro, com os nodos B, E, I e K, e, por fim, o quarto nível, com os nodos A, 
C e F. O grau de um nodo é a quantidade de filhos que possui. Por exemplo, 
o nodo G tem grau dois, o nodo E tem grau um e o nodo A tem grau zero. Por 
ser uma árvore binária, não há qualquer nodo com grau maior do que dois 
(FERRARI et al., 2014).
Como árvores são estruturas não lineares, diversos caminhos podem 
ser percorridos a partir da raiz. Feofiloff (2008) define um caminho em uma 
árvore como uma sequência de nodos (a0, a1, ..., an), onde an+1 é filho de an 
e n é o tamanho do caminho. Um possível caminho da árvore da Figura 2 é 
(G, D, E, F). Esse caminho tem tamanho quatro. Um caminho em uma árvore 
Árvores binárias em Java 3
que percorre todos seus nodos uma única vez é chamado de caminhamento. 
Existem três tipos de caminhamentos: prefixado, pós-fixado e em ordem. O 
que difere em cada um desses caminhamentos é a forma como percorrem os 
nodos da árvore (EDELWEISS; GALANTE, 2008). Vejamos, a seguir, como cada 
um desses caminhos funciona. 
 � Caminhamento prefixado: nesse caminhamento, é visitada a raiz da 
árvore, depois a subárvore da esquerda e, em seguida, a subárvore da 
direita. Considerando a árvore da Figura 2, o caminhamento prefixado 
visitaria os nodos na seguinte ordem: G–D–B–A–C–E–F–J–I–K.
 � Caminhamento pós-fixado: a ordem de visita dos nodos no caminha-
mento pós-fixado consiste em visitar inicialmente a subárvore da 
esquerda, depois a subárvore da direita e, por fim, a raiz. Na árvore da 
Figura 2, o caminhamento pós-fixado produziria a seguinte sequência 
de nodos: A–C–B–F–E–D–I–K–J–G.
 � Caminhamento em ordem: o caminhamento em ordem visita, inicial-
mente, a subárvore da esquerda, depois visita a raiz e, finalmente, 
visita a subárvore da direita. No exemplo da Figura 2, o caminhamento 
em ordem naquela árvore seria: A–B–C–D–E–F–G–I–J–K.
Note que a definição de caminhamentos é recursiva. Portanto, quando 
visitamos uma de suas subárvores, esta é tratada como uma árvore 
também. Por exemplo, na árvore da Figura 2, ao visitar a subárvore esquerda da raiz 
G, obtemos uma árvore com raiz em D e, da mesma forma, ao visitar a subárvore 
esquerda do nodo D, obtemos uma nova árvore com raiz em B. Como veremos 
mais adiante neste capítulo, a maioria dos algoritmos que operam sobre árvores 
é escrita de maneira recursiva: acostume-se a enxergar árvores dessa forma.
Operações sobre uma árvore binária
Nesta seção, vamos ver como as operações básicas de uma estrutura de dados, 
a inserção e a remoção de elementos, podem ser implementadas em uma 
árvore binária. Consideraremos que as árvores tratadas são ordenadas, isto é, 
que existe uma relação de ordem para suas subárvores (EDELWEISS; GALANTE, 
2008). Ainda, utilizaremos um tipo de árvore binária chamado de árvore binária 
de busca para definir tais operações. Além de ser bastante utilizado, esse tipo 
de árvore possui uma regra de ordenação simples: todo nodo deve possuir 
Árvores binárias em Java4
uma chave de pesquisa, que permite comparar os nós da árvore. Ao inserir um 
valor na árvore, o nodo é comparado com a raiz e é inserido na subárvore da 
esquerda, se for menor do que a raiz. Caso sua chave possua um valor maior 
do que o da raiz, nodo é inserido na subárvore da direita (FEOFILOFF, 2008).
A árvore mostrada na Figura 2 é considerada um exemplo de árvore binária 
de busca, considerando a ordem lexicográfica para comparar as letras que 
foram inseridas. Veja como os nodos da subárvore da esquerda do nodo G 
somente possuem elementos menores do que G e como o oposto ocorre na 
subárvore da direita. Dado esse critério de ordenação, realizar pesquisas na 
árvore torna-se bastante eficiente. Por exemplo, imagine que queremos en-
contrar o nodo E na árvore da Figura 2. Começando na raiz da árvore, sabemos 
que esse nodo, se estiver na árvore, estará na subárvore da esquerda, pois 
E < G. Prosseguindo a busca nessa subárvore, comparamos E com D. Nesse 
caso, concluímos que E deve estar na subárvore da direita de D, pois E > D, 
que é onde vamos encontrar o nodo contendo o valor E.
Vamos, agora, definir como um nodo de uma árvore binária deve ser cons-
truído. Por ser uma árvore binária, devemosincluir dois apontadores, um para 
a subárvore da esquerda e outro para a subárvore da direita, de modo a criar 
o encadeamento entre os nodos, conforme mostra a Figura 3. Além disso, cada 
nodo conterá uma seção para armazenar dados. Destacamos, aqui, o campo 
chave do nodo, que é obrigatório para árvores binárias de busca. Outros campos 
de dados podem ser adicionados, de acordo com a necessidade.
Figura 3. Nodo de uma árvore binária.
Inserção de nodos em uma árvore binária
Para inserir um novo nodo em uma árvore binária, precisamos, inicialmente, 
testar se a raiz da árvore está vazia. Se estiver, alocamos o novo nodo como 
a raiz da árvore. Caso a raiz exista, comparamos sua chave com o valor da 
chave do novo nodo. Se a chave do novo nodo for menor do que a raiz e ela 
ainda não possuir um fi lho na subárvore da esquerda, encadeamos o novo 
Árvores binárias em Java 5
nodo na subárvore da esquerda. Se o valor da chave do novo nodo for maior 
do que o da raiz e se a raiz ainda não possuir um filho na subárvore da direita, 
então o novo nodo será encadeado nessa subárvore.
Caso exista um filho na subárvore em que será feita a inserção, o filho se 
torna a raiz da árvore e repete-se, recursivamente, o processo de decidir se 
a inserção acontecerá na subárvore da direita ou da esquerda, até encontrar 
um local onde deva ser inserido o novo nodo. Veja, abaixo, um algoritmo 
recursivo que insere um novo nodo na árvore binária:
função insere(Nodo raiz, int valor){
 se (raiz == NULL){
 Nodo novo = Nodo(valor)
 raiz = novo;
 } senao se (raiz.chave > valor){
 se (raiz.filhoEsquerdo == NULL){
 Nodo novo = Nodo(valor)
 raiz.filhoEsquerdo = novo
 } senao {
 insere(raiz.filhoEsquerdo, valor)
 }
 } senao se (raiz.chave < valor){
 se (raiz.filhoDireito == NULL){
 Nodo novo = Nodo(valor)
 raiz.filhoDireito = novo
 } senao {
 insere(raiz.filhoDireito, valor)
 }
 }
}
Árvores binárias em Java6
Para exemplificar, considere uma árvore que está inicialmente vazia. 
Agora, vamos realizar a inserção do nodo com chave 5 nessa árvore. 
Como ela está vazia, esse nodo será a raiz. Na sequência, se formos 
inserir o nodo 3, este será alocado à esquerda do nodo 5, pois é menor. 
Inserindo o valor 2 na árvore, necessita-se comparar 2 com 5. Como 
2 é menor, ele será inserido na subárvore da esquerda, mas, como já 
existe um nodo nessa subárvore, agora comparamos o nodo 2 com o 
nodo 3. Como 2 também é menor, inserimos esse nodo na subárvore da 
esquerda do nodo 3. Seguindo essa regra, podemos inserir o nodo 4 à 
direita do nodo 3, o nodo 8 à direita do nodo 5, o nodo 6 à esquerda 
do nodo 8 e, por fim, o nodo 7 à direita do nodo 6. A árvore resultante 
dessas inserções pode ser vista na Figura 4.
Figura 4. Árvore binária resultante da inserção dos nodos 5–3–2–4–8–6–7, nessa ordem.
Há duas coisas que são interessantes de se observar na inserção de nodos 
em uma árvore binária. A primeira delas é que os novos nodos são sempre 
inseridos nas folhas da árvore. Logo, chamamos o método inserir recursi-
vamente até encontrar um nodo que não tenha filhos para fazer a inserção. 
Também é interessante destacar que a ordem de inserção dos nodos em 
uma árvore influencia sua aparência. Por exemplo, se repetirmos o exemplo 
anterior, mas inserindo os nodos na ordem contrária na árvore, esta teria 
uma aparência diferente. Veja, na Figura 5, como ficaria essa árvore.
Árvores binárias em Java 7
Figura 5. Árvore binária resultante da inserção dos nodos 7–6–8–4–2–3–5, nessa ordem. 
Perceba como a mudança na ordem de inserção modifica a aparência da árvore.
Remoção de nodos em uma árvore binária
A remoção de um nodo da árvore binária é mais complexa do que a inserção, 
pois devemos garantir que, após a remoção, a árvore se manterá ordenada, 
e por isso temos uma quantidade maior de casos que precisam ser conside-
rados. O primeiro passo é buscar, na árvore, o valor que se deseja remover. 
Após, analisamos em qual dos casos a seguir ele se encaixa e realizamos a 
deleção do nodo. O caso mais simples de remoção é quando há um único 
nodo na árvore, bastando somente setar a raiz da árvore com o valor nulo. 
Quando a árvore possui um nodo filho, três situações distintas podem 
ocorrer no momento da remoção, de acordo com o grau do nodo que será 
removido. Vejamos.
 � Remoção de nodo de grau zero: remover um nodo folha (grau zero) é 
simples; basta encontrar seu pai e setar sua referência no nodo pai 
para nulo.
 � Remoção de nodo de grau um: quando o nodo que será removido 
possui uma subárvore, seu filho passa a ocupar seu local na árvore; 
ou seja, encadeamos o nodo avô com seu neto, removendo o nodo 
intermediário.
 � Remoção de nodo de grau dois: quando ambas as subárvores do nodo 
que será removido estão ocupadas, existem dois nodos que podem 
Árvores binárias em Java8
ser escolhidos para substituir o nodo que será removido, (1) o maior 
valor da subárvore da esquerda e (2) o menor valor da subárvore da 
direita. Escolher um desses dois nodos garantirá que a árvore conti-
nue ordenada. Por convenção, neste capítulo, adotaremos a primeira 
opção. Para encontrar o maior valor da subárvore da esquerda, basta 
encontrar o valor que está mais à direita nessa subárvore.
Veja, abaixo, o algoritmo que realiza a exclusão de um nodo em uma ár-
vore. Foram definidas algumas funções auxiliares, como a função pesquisa, 
que pesquisa se um valor está ou não na árvore, retornando seu nodo (ela 
retorna NULL se o nodo não estiver na árvore). Também definimos a função 
busca _ pai, que retorna o pai do nodo que contém determinado valor. Por 
fim, a função max é utilizada para buscar o valor máximo da subárvore da 
esquerda quando ocorre uma remoção de nodo com dois filhos.
função remove(Nodo raiz, int valor){
 Nodo del = pesquisa(raiz, valor)
 se (del == NULL){
 escreva(“O valor procurado não está na árvore.”)
 } senao se (del.filhoEsquerdo == NULL e del.filhoDi-
reito == NULL){
 // Remoção de um nodo folha
 Nodo pai = busca _ pai(valor)
 se (pai.FilhoEsquerdo.chave == valor) 
 pai.filhoEsquerdo = NULL
 senao
 pai.filhoDireito = NULL
 } senao se (del.FilhoEsquerdo != NULL e del.filhoDi-
reito != NULL){
 // Remoção de Nodo de grau 2
 Nodo pai = busca _ pai(valor)
Árvores binárias em Java 9
 Nodo maior = max(del.filhoEsquerdo)
 Nodo pai _ maior = busca _ pai(maior.chave)
 // Transfere os dados do maior para o del
 del.chave = maior.chave
 // Apaga o nodo maior
 se (maior.filhoEsquerdo == NULL e maior.filhoDireito 
== NULL){
 se (pai _ maior.FilhoEsquerdo.chave == maior){
 pai _ maior.FilhoEsquerdo = NULL
 } senao {
 Pai _ maior.FilhoDireito = NULL
 }
 } senao {
 // Maior possui um filho a esquerda
 se (pai _ maior.FilhoEsquerdo.chave == maior){
 pai _ maior.FilhoEsquerdo = maior.
FilhoEsquerdo
 } senao {
 Pai _ maior.FilhoDireito = maior.FilhoEsquerdo
 }
 }
 } senao {
 // Remoção de nodo de grau 1
Árvores binárias em Java10
 Nodo pai = busca _ pai(valor)
 se (pai.FilhoEsquerdo.chave == valor) {
 se (del.filhoEsquerdo != NULL) 
 pai.filhoEsquerdo = del.filhoEsquerdo
 senao
 pai.filhoEsquerdo = del.filhoDireito
 } senao {
 se (del.filhoEsquerdo != NULL) 
 pai.filhoDireito = del.filhoEsquerdo
 senao
 pai.filhoDireito = del.filhoDireito 
 }
 } 
}
Vamos exemplificar cada caso de remoção com um exemplo prático. 
Considere a árvore obtida na Figura 4. Vamos remover os nodos 7, 8 e 3 da 
árvore, nessa ordem. O nodo 7 possui grau zero; logo, para que seja removido 
da árvore, basta apagar a referência de seu pai (nodo 6) para ele, resultando 
na árvore da Figura 6a. A remoção do nodo 8 se encaixa no segundo caso, 
visto que esse nodo possui uma subárvore. Nesse caso, devemos modificar 
a referência de seu pai (nodo 5) para o nodo 6, que é o filhodo nodo 8. Esse 
processo resultará na árvore da Figura 6b. Por fim, removendo o nodo 3, que 
possui dois filhos, demanda a escolha do maior nodo da subárvore da esquerda 
para substituí-lo. Como, nesse exemplo, só existe o nodo 2 na subárvore, ele 
substituirá o nodo 3, produzindo a árvore da Figura 6c.
Árvores binárias em Java 11
Figura 6. Remoção de nodos 7 (a), 8 (b) e 3 (c) de uma árvore binária.
Implementando uma árvore binária em Java
Após estudarmos a terminologia básica e conhecer os principais métodos 
presentes em uma árvore binária, podemos implementar essa estrutura de 
dados na linguagem de programação Java, utilizando orientação a objetos. 
Aqui, vamos criar uma árvore que armazena somente um número inteiro, que 
também será a chave de busca na árvore. Iniciaremos definindo uma classe 
que representa um nodo da árvore, chamada de NodoArvore, conforme 
mostrado na Figura 3.
public class NodoArvore { 
 private NodoArvore filhoEsquerda; 
 private int chave; 
 private NodoArvore filhoDireita; 
 
 public NodoArvore(int valor){ 
Árvores binárias em Java12
 this.setChave(valor); 
 this.setFilhoEsquerda(null); 
 this.setFilhoDireita(null); 
 }
}
Considere que essa classe também contém os getters e setters correspon-
dentes aos três campos presentes na classe. Ao criar um nodo na árvore, é 
necessário informar o valor inteiro que será armazenado naquele nodo. Por 
padrão, os filhos esquerdo e direito daquele nodo são nulos. Vamos definir, 
agora, uma interface que contém os métodos públicos que devem estar 
presentes na estrutura de dados árvore:
public interface Arvore { 
 public NodoArvore pesquisa (int valor);
 public void inserir (int valor); 
 public void remover (int valor); 
 public void imprime _ preFixado(); 
}
Essa interface possui quatro métodos: um método de consulta à árvore, 
chamado de pesquisa, os métodos de inserção e remoção, e, por fim, um mé-
todo que imprime os valores presentes na árvore utilizando o caminhamento 
prefixado. Vamos, agora, definir a classe ArvoreBinaria, que implementa 
a interface Arvore definida anteriormente e contém uma instância de No-
doArvore, que será a raiz da árvore. Esse objeto é inicializado com o valor 
NULL no construtor da classe.
public class ArvoreBinaria implements Arvore { 
 private NodoArvore raiz; 
 
 public ArvoreBinaria(){ 
 this.raiz = null; 
 }
 
 // Os métodos da interface Arvore deverão ser inseri-
dos aqui.
}
Árvores binárias em Java 13
Vejamos, agora, a implementação de cada um dos métodos da interface 
Arvore, bem como de alguns métodos auxiliares que permanecerão com a 
visibilidade privada dentro da classe ArvoreBinaria para que não sejam 
utilizados fora dessa classe.
Método pesquisa na árvore binária
O método de busca recebe, como parâmetro, o valor que se quer encontrar 
na árvore. Ele utiliza um método auxiliar recursivo para percorrer a árvore 
e buscar o valor informado. O método pesquisaRecursivo compara o 
valor buscado com o valor da chave da raiz. Se eles forem iguais, o método 
retorna aquele nodo; caso contrário, é checado se o valor informado como 
parâmetro é maior ou menor do que a chave do nodo atual. Caso seja 
menor, sabemos que o valor buscado só poderá estar na subárvore da 
esquerda e, se for maior, na subárvore da direita. Conforme o resultado do 
método, é feita uma chamada recursiva, tendo como parâmetro a subárvore 
correspondente ao resultado do teste. Veja, abaixo, a implementação dos 
métodos descritos.
public NodoArvore pesquisa(int valor){ 
 NodoArvore nodoResultado = pesquisaRecursivo(raiz, 
valor); 
 return nodoResultado; 
} 
 
private NodoArvore pesquisaRecursivo(NodoArvore raiz, int 
valor) { 
 if(raiz != null) { 
 if(valor == raiz.getChave()){ 
 return raiz; 
 } else if (valor < raiz.getChave()) { 
 return pesquisaRecursivo(raiz.getFilhoEs-
querda(), valor); 
 } else { 
 return pesquisaRecursivo(raiz.getFilhoDi-
reita(), valor); 
 } 
 } 
 return null; 
}
Árvores binárias em Java14
Método de impressão da árvore
A impressão da árvore é feita por meio de um caminhamento prefixado. 
O método imprime _ preFixado faz uma chamada ao método recursivo 
preFixado, informando, como parâmetro, a raiz da árvore. Esse método, 
por sua vez, realiza o caminhamento pela árvore. Se o nodo atual é nulo, 
nada é feito; caso contrário, ele imprime o valor presente na raiz e faz duas 
chamadas recursivas, primeiramente para a subárvore da esquerda e, depois, 
para a subárvore da direita.
public void imprime _ preFixado(){ 
 preFixado(this.raiz); 
} 
 
private void preFixado(NodoArvore raiz){ 
 if( raiz == null ) 
 return; 
 
 System.out.print(raiz.getChave() + " "); 
 preFixado(raiz.getFilhoEsquerda()); 
 preFixado(raiz.getFilhoDireita()); 
}
Inserção de nodos na árvore
Agora vamos analisar o método de inserção em uma árvore binária. O método 
inserir recebe um número inteiro como parâmetro e o insere na árvore. O 
primeiro teste que é feito é se a árvore está vazia. Se for o caso, então esse 
valor passa a ser a nova raiz da árvore. Se já houver uma raiz, é invocado o 
método recursivo insere, que vai procurar o local onde o novo nodo deve 
ser inserido a partir da raiz, de acordo com o critério de ordenação da árvore. 
Se o valor for menor do que a raiz, ele deve ser inserido ao lado esquerdo 
da árvore; caso seja maior, ele será inserido no lado direito. Após decidir em 
qual das subárvores o nodo deverá ser inserido, é necessário verificar se já 
existe algum nodo alocado naquela subárvore. Caso não exista, criamos um 
nodo e o inserimos naquela subárvore. Se existir um nodo, fazemos uma 
chamada recursiva à função insere, considerando o nodo da subárvore 
correspondente como raiz.
Árvores binárias em Java 15
public void inserir (int valor){ 
 if (raiz == null){ 
 raiz = new NodoArvore(valor); 
 } else { 
 insere(raiz, valor); 
 } 
}
private void insere (NodoArvore raiz, int valor){ 
 if (valor < raiz.getChave()){ 
 if (raiz.getFilhoEsquerda() == null){ 
 NodoArvore novoNodo = new NodoArvore(valor); 
 raiz.setFilhoEsquerda(novoNodo); 
 } else { 
 insere(raiz.getFilhoEsquerda(), valor); 
 } 
 } else if (valor > raiz.getChave()){ 
 if (raiz.getFilhoDireita() == null){ 
 NodoArvore novoNodo = new NodoArvore(valor); 
 raiz.setFilhoDireita(novoNodo); 
 } else { 
 insere(raiz.getFilhoDireita(), valor); 
 } 
 } 
}
Remoção de nodo da árvore
Por fim, vejamos como implementar a remoção de nodos de uma árvore. 
Como comentado na seção anterior, o método de remoção tem alguns casos 
especiais que precisam ser tratados. Estudaremos o código de cada um deles 
agora. Iniciaremos com o método remover, que decide qual dos casos deve 
ser tratado. Primeiramente, utilizaremos o método pesquisa para verificar 
se o valor informado como parâmetro da função realmente se encontra na 
árvore. Se ele não estiver na árvore, apenas informamos ao usuário, escre-
vendo uma mensagem na tela. Se o valor for encontrado na árvore, verificamos 
quantos filhos ele possui e chamamos o método adequado: removeFolha, 
removeUmFilho ou removeDoisFilhos.
Árvores binárias em Java16
public void remover (int valor){ 
 NodoArvore nodo = pesquisa(valor); 
 if (nodo == null){ 
 System.out.println("O valor "+ valor +" não está 
na árvore."); 
 } else if (nodo.getFilhoEsquerda() == null && 
 nodo.getFilhoDireita() == null){ 
 removeFolha(nodo); 
 } else if (nodo.getFilhoEsquerda() != null && 
 nodo.getFilhoDireita() != null){ 
 removeDoisFilhos(nodo); 
 } else { 
 removeUmFilho(nodo); 
 } 
}
Vejamos, agora, como se dá a remoção do nodoem cada um dos três casos. 
Antes disso, vamos definir um método auxiliar chamado de buscaPai, que 
encontra o pai do nodo que deve ser removido. Esse método tem funciona-
mento parecido com o método pesquisa; porém, no lugar de verificar se a 
raiz corresponde ao valor atual, ele checa se algum dos filhos possui o valor 
buscado. Esse método retorna null caso o nodo buscado não possua pai e, 
nesse caso, saberemos que se trata do nodo-raiz.
private NodoArvore buscaPai(NodoArvore raiz, int valor) { 
 if (raiz != null){ 
 if (valor < raiz.getChave()){ 
 if (raiz.getFilhoEsquerda() != null && 
 raiz.getFilhoEsquerda().ge-
tChave() == valor){ 
 return raiz; 
 } else { 
 return buscaPai(raiz.getFilhoEsquerda(), 
valor); 
 } 
 } else if (valor > raiz.getChave()){ 
 if (raiz.getFilhoDireita() != null && 
 raiz.getFilhoDireita().getChave() == valor){ 
 return raiz; 
 } else { 
Árvores binárias em Java 17
 return buscaPai(raiz.getFilhoDireita(), valor); 
 } 
 } 
 } 
 return null; 
}
O método que remove um nodo folha é descrito abaixo. Usamos o método 
buscaPai para localizar o pai do nodo que será removido e, em seguida, 
fazemos um teste para decidir se o nodo a ser removido está à esquerda 
ou à direita de seu pai. Por fim, apagamos sua referência, setando o valor 
daquela subárvore para null.
private void removeFolha(NodoArvore nodo){ 
 NodoArvore pai = buscaPai(this.raiz, nodo.getChave()); 
 if (pai == null){ 
 this.raiz = null; 
 } else if (pai.getFilhoEsquerda() != null && 
 pai.getFilhoEsquerda() == nodo){ 
 pai.setFilhoEsquerda(null); 
 } else { 
 pai.setFilhoDireita(null); 
 } 
}
Observe a comparação que fizemos na função removeFolha: pai.
getFilhoEsquerda() == nodo. Nesse tipo de comparação, 
não estamos comparando os valores das chaves dos dois nodos, mas suas 
referências. Isso quer dizer que essa expressão retornará o valor true se pai.
getFilhoEsquerda() e nodo forem o mesmo objeto. Caso sejam objetos 
diferentes, essa comparação retornará o valor false. 
Veja, agora, como fica a implementação do método que remove um nodo 
que possua um filho. Primeiramente, buscamos o pai do nodo que será remo-
vido e, após isso, verificamos se estamos removendo a raiz da árvore. Se for 
verdadeiro, o filho passa a ser a nova raiz; caso contrário, encadeamos o nodo 
avô com seu neto, removendo o nodo informado como parâmetro da árvore.
private void removeUmFilho(NodoArvore nodo){ 
 NodoArvore pai = buscaPai(this.raiz, nodo.getChave()); 
 if (pai == null){ 
Árvores binárias em Java18
 if (nodo.getFilhoEsquerda() != null){ 
 this.raiz = nodo.getFilhoEsquerda(); 
 } else { 
 this.raiz = nodo.getFilhoDireita(); 
 } 
 } else if (nodo.getFilhoEsquerda() != null) { 
 if (pai.getFilhoEsquerda() == nodo){ 
 pai.setFilhoEsquerda(nodo.getFilhoEsquerda()); 
 } else { 
 pai.setFilhoDireita(nodo.getFilhoEsquerda()); 
 } 
 } else if (nodo.getFilhoDireita() != null){ 
 if (pai.getFilhoEsquerda() == nodo){ 
 pai.setFilhoEsquerda(nodo.getFilhoDireita()); 
 } else { 
 pai.setFilhoDireita(nodo.getFilhoDireita()); 
 } 
 } 
}
O método que exclui um nodo que possui dois filhos necessita de 
um método auxiliar max, que retorna o maior valor de uma árvore. Esse 
método será utilizado para encontrar o nodo que deverá substituir o 
nodo que será removido, conforme explicado na seção anterior. Seu 
funcionamento é bastante simples: ele percorre a subárvore da direita 
até encontrar um nodo que não possua um filho. Esse nodo contém o 
maior valor naquela árvore.
private NodoArvore max(NodoArvore raiz){ 
 if (raiz.getFilhoDireita() == null){ 
 return raiz; 
 } else { 
 return max(raiz.getFilhoDireita()); 
 } 
}
Finalmente, vejamos o código correspondente ao método removeDois-
Filhos. A princípio, esse método procura o pai do nodo a ser removido 
e o maior nodo de sua subárvore esquerda, que ocupará seu lugar. Além 
disso, buscamos o pai do maior nodo, para corrigirmos suas referências. 
Na sequência, modificamos o valor da chave do nodo que será removido 
Árvores binárias em Java 19
(e outros dados que também estejam armazenados naquele nodo) para o 
valor da chave (e outros dados) do nodo max e deletamos o maior nodo 
da árvore. Dessa forma, não precisaremos fazer muitas alterações nos 
apontadores dos nós. 
Por fim, procedemos com a remoção do nodo max da árvore. Será neces-
sário fazer um teste para verificar se esse nodo possui algum descendente 
à esquerda (sabemos que, por ser o maior valor da árvore, ele não possui 
qualquer filho à direita). Caso ele não tenha, basta apagar sua referência 
na variável paiMax. Se ele possuir um filho, esse nodo passa a ocupar sua 
posição.
private void removeDoisFilhos(NodoArvore nodo){ 
 NodoArvore pai = buscaPai(this.raiz, nodo.getChave()); 
 NodoArvore max = max(nodo.getFilhoEsquerda()); 
 NodoArvore paiMax = buscaPai(nodo, max.getChave()); 
 nodo.setChave(max.getChave()); 
 if (max.getFilhoEsquerda() == null){ 
 if (paiMax == nodo){ 
 paiMax.setFilhoEsquerda(null); 
 } else { 
 paiMax.setFilhoDireita(null); 
 } 
 } else { 
 paiMax.setFilhoDireita(max.getFilhoEsquerda()); 
 } 
}
Referências
DEITEL, H. M.; DEITEL, P. J. JavaTM: como programar. 10. ed. São Paulo: Pearson, 2017. 
EDELWEISS, N.; GALANTE, R. Estruturas de dados. Porto Alegre: Bookman, 2008. 18 v. 
(Série Livros Didáticos informática UFRGS).
FEOFILOFF, P. Algoritmos em linguagem C. Rio de Janeiro: Elsevier, 2008. 
FERRARI, R. et al. Estruturas de dados com jogos. Rio de Janeiro: Elsevier, 2014. 
GOODRICH, M. T.; TAMASSIA, R. Estrutura de dados e algoritmos com Java. 5. ed. Porto 
Alegre: Bookman, 2013.
TANENBAUM, A. S.; WOODHULL, A. S. Sistemas operacionais: projeto e implementação. 
Porto Alegre: Bookman, 2008. 
Árvores binárias em Java20
Os links para sites da web fornecidos neste capítulo foram todos 
testados, e seu funcionamento foi comprovado no momento da 
publicação do material. No entanto, a rede é extremamente dinâmica; suas 
páginas estão constantemente mudando de local e conteúdo. Assim, os editores 
declaram não ter qualquer responsabilidade sobre qualidade, precisão ou 
integralidade das informações referidas em tais links.
Árvores binárias em Java 21

Mais conteúdos dessa disciplina