Trabalhando com árvores binárias em Java

Veja neste artigo o que é uma árvore binária, suas propriedades, seus usos nas aplicações existentes e exemplos de implementação e execução.

Existem os mais diferentes tipos de árvores, no entanto, as árvores binárias são especiais e muito utilizadas nas mais diversas aplicações porque quando ordenadas permitem que pesquisas, inclusões e exclusões de dados em sua estrutura sejam extremamente rápidas.

Conceitos

As árvores são estruturas de dados baseadas em listas encadeadas que possuem um nó superior também chamado de raiz que aponta para outros nós, chamados de nós filhos, que podem ser pais de outros nós.

Uma árvore de busca binária tem as seguintes propriedades:

Segue na Figura 1 uma ilustração de um exemplo de árvore binária.

Figura 1. Exemplo ilustrativo de uma Árvore Binária

No exemplo acima tem-se uma árvore binária onde a raiz é o elemento 8, o filho da esquerda do elemento 8 é o elemento 3, o filho da direita é o elemento número 10. Nota-se que todos elementos da árvore binária possuem no máximo dois filhos, sendo o da esquerda sempre menor e o da direita sempre maior que o elemento pai.

Implementação

Agora que já se conhece um pouco mais sobre o que é e como funciona uma árvore binária, vejamos abaixo os algoritmos para inserção e exclusão em árvores binárias implementados na linguagem Java.

Segue na Listagem 1 o código de exemplo para inserção em árvores binárias.

public void inserir(No node, int valor) { //verifica se a árvore já foi criada if (node != null) { //Verifica se o valor a ser inserido é menor que o nodo corrente da árvore, se sim vai para subárvore esquerda if (valor < node.valor) { //Se tiver elemento no nodo esquerdo continua a busca if (node.esquerda != null) { inserir(node.esquerda, valor); } else { //Se nodo esquerdo vazio insere o novo nodo aqui System.out.println(" Inserindo " + valor + " a esquerda de " + node.valor); node.esquerda = new No(valor); } //Verifica se o valor a ser inserido é maior que o nodo corrente da árvore, se sim vai para subárvore direita } else if (valor > node.valor) { //Se tiver elemento no nodo direito continua a busca if (node.direita != null) { inserir(node.direita, valor); } else { //Se nodo direito vazio insere o novo nodo aqui System.out.println(" Inserindo " + valor + " a direita de " + node.valor); node.direita = new No(valor); } } } }
Listagem 1. Exemplo de inserção em árvores binárias

Pode-se notar que o algoritmo funciona recursivamente, chamando a função repetidas vezes. Os detalhes do código estão exemplificados através dos comentários no seu corpo.

Na Listagem 2 apresenta-se um algoritmo alternativo para exclusão em árvores binárias.

public No removeValorMinimoDaArvore(No node) { if (node == null) { System.out.println(" ERRO "); } else if (node.esquerda != null) { node.esquerda = removeValorMinimoDaArvore(node.esquerda); return node; } else { //Se não tiver elemento esquerdo só nos resta o da direita return node.direita; } return null; }
Listagem 2. Exemplo de exclusão em árvores binárias

No código acima nota-se que o menor elemento da árvore está sendo excluído, o código também poderia ser implementado recebendo um nó a ser excluído, procurar este nó e excluir da árvore. O caminhamento em árvores será descrito a seguir. Nota-se que os elementos filhos tomam a posição do elemento excluído.

Caminhamentos

Uma árvore binária pode ser percorrida utilizando caminhamento prefixado, pós-fixado e em ordem, todos serão detalhados a seguir.

Caminhamento Prefixado

Basicamente no caminhamento prefixado será utilizado o algoritmo abaixo:

O algoritmo utilizando recursão torna-se extremamente simples. Portanto vejamos na Listagem 3 e na prática como isso é feito em linguagem Java, dado que o método recebe a raiz da árvore.

public void prefixado(No no) { if(no != null){ System.out.print(no.valor + " "); prefixado(no.esquerda); prefixado(no.direita); } }
Listagem 3. Exemplo de caminhamento prefixado em árvores binárias

Para o exemplo da árvore acima ter-se-ia a seguinte ordem de visita dos nós: 8, 3, 1, 6, 4, 7, 10, 14, 13.

Para o leitor entender um pouco melhor como funciona esse algoritmo e os demais para caminhamentos em árvores binárias, será detalhada a pilha de chamadas para esta árvore. Portanto, para a árvore dada anteriormente temos a seguinte linha de execução:

Caminhamento Pós-fixado

Basicamente no caminhamento Pós-fixado será utilizado o algoritmo abaixo:

Pois vejamos também para o algoritmo acima na prática como isso é feito em linguagem Java, dado que o método recebe a raiz da árvore.

public void posfixado(No no) { if(no != null){ posfixado(no.esquerda); posfixado(no.direita); System.out.print(no.valor + " "); } }
Listagem 4: Exemplo de caminhamento pósfixado em árvores binárias

Para o exemplo da árvore acima ter-se-ia a seguinte ordem de visita dos nós: 1, 4, 7, 6, 3, 13, 14, 10, 8.

Baseando-se na pilha de execução explicada anteriormente fica muito simples de entender o que esse algoritmo faz e qual ordem ele segue.

Caminhamento Em Ordem

Basicamente no caminhamento Em Ordem será utilizado o algoritmo abaixo:

Então vejamos também para o algoritmo acima na prática como isso é feito em linguagem Java, dado que o método também recebe a raiz da árvore.

public void emordem(No no) { if(no != null){ emordem(no.esquerda); System.out.print(no.valor + " "); emordem(no.direita); } }
Listagem 5: Exemplo de caminhamento em ordem em árvores binárias

Para o exemplo da árvore acima ter-se-ia a seguinte ordem de visita dos nós: 1, 3, 4, 6, 7, 8, 10, 13, 14.

Aplicações

Árvores binárias são largamente utilizadas em diversas aplicações. Entre as aplicações pode-se citar as árvores de decisão usadas na Inteligência Artificial. Outra aplicação é na representação de expressões aritméticas. No caso da representação das expressões aritméticas pode-se utilizar um caminhamento pós-fixado para resolver o problema, onde, por exemplo, para uma árvore binária de expressões aritméticas ter-se-ia para cada nó externo um valor associado e para cada nó interno um operador aritmético associado, esse algoritmo calcularia facilmente o resultado da expressão. Sugerimos ao leitor tentar este exercício e em caso de dúvidas contate o autor para fornecer as respostas para as dúvidas.

Conclusão

Neste artigo procurou-se demonstrar alguns conceitos e como essa importante estrutura de dados é implementada na prática na linguagem de programação Java. As suas vantagens, peculiaridades que lhe fazem ser uma árvore binária, sua implementação e detalhes da sua execução foram demonstradas neste artigo.

Pessoal, como observação reitero que eu não coloquei o código completo com a execução (a partir de um método main) e a estrutura do nó por ser extremamente simples e para fins de não alongar muito o tamanho do artigo. Se quiserem o código completo não deixem de me contatar no meu e-mail que eu passo o código sem problemas. Continuo atendendo às solicitações de artigos dos amigos leitores, este é mais um dos tantos requeridos e espero outra sugestões.

Bibliografia

PREISS, Bruno R. Estruturas de dados e algoritmos: Padrões de projetos orientados a objeto com Java; tradução de Elizabeth Ferreira. Rio de Janeiro: Campus, 2000.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados