Olá amados leitores. Hoje iremos estudar um tema muito interessante nas estruturas de dados, as árvores binárias. Para você que já conhece toda a parte teórica da coisa e só quer um exemplo prático, você pode clicar aqui e baixar um projeto pronto.
Ahhhhhhhhhhhhhhhhh, antes que alguém me pergunte: "Ainnn João, você vai falar sobre balanceamento ?"
Eu já respondo: "Não vou falar porque senão o post vai ficar extremamente extenso". Você vai ter que correr por fora amigo.
Vamos começar então ?!
Você sabe o que é uma árvore binária ?
Não !?
Tudo bem amigo, vamos facilitar um pouco mais a pergunta. To pegando pesado !
Você sabe o que é um árvore (não me refiro a planta) ?
Também não !?
Bem, então, senta aí que lá vem história. Pegue seu café e acenda seu cigarro. Uma árvore, não é nada mais nada menos que, um grafo acíclico. Mas aí você me pergunta: O que é um grafo ?
Vamos descer mais um degrau. Segundo à Wikipedia, a teoria dos grafos é um ramo da matemática que estuda as relações entre os objetos de um determinado conjunto. Para representar tais relações, são usados estruturas chamadas grafos, G(V,A) onde, G é um conjunto de vértices e arestas. Os vértices são um conjunto não vazio dos vértices do grafo. As arestas são um conjunto de pares não ordenados de V. Vamos ao exemplo.
- O primeiro nó de uma árvore é o nó raiz.
- Nó folha é todo nó que não possui conexões.
- Em uma árvore existe 1 e somente 1 caminho que liga um nó ao outro.
- Dado um determinado vértice, cada "filho" seu é a "raíz" de uma nova "sub-árvore".
- Grau de um vértice é o número de sub-árvores do vértice.
- Altura da árvore é o comprimento do caminho mais longo da raiz até uma das suas folhas.
- O nível de um nó é o número de nós no caminho entre o vértice e a raiz.
package pkg;
public class Node {
private Integer valor;
private Node noEsquerda;
private Node noDireita;
public Node() { }
public Node(Integer valor) {
super();
this.valor = valor;
}
public Integer getValor() {
return valor;
}
public void setValor(Integer valor) {
this.valor = valor;
}
public Node getNoEsquerda() {
return noEsquerda;
}
public void setNoEsquerda(Node noEsquerda) {
this.noEsquerda = noEsquerda;
}
public Node getNoDireita() {
return noDireita;
}
public void setNoDireita(Node noDireita) {
this.noDireita = noDireita;
}
@Override
public String toString() {
return "Node [valor=" + valor + "]";
}
}
package pkg;
public class BinaryTree {
private Node root;
public boolean isEmpty(){
if(root == null){
return true;
}
return false;
}
public int getAltura(){
return getAltura(this.root);
}
private int getAltura(Node root){
if(root == null){
return 0;
}
int altEsq = getAltura(root.getNoEsquerda());
int altDir = getAltura(root.getNoDireita());
if(altEsq > altDir){
return altEsq + 1;
} else {
return altDir + 1;
}
}
public int getQtdNode(){
return getQtdNode(root);
}
private int getQtdNode(Node root){
if(root == null){
return 0;
}
int qtdNodeEsq = getQtdNode(root.getNoEsquerda());
int qtdNodeDireita = getQtdNode(root.getNoDireita());
return qtdNodeEsq + qtdNodeDireita + 1;
}
public void imprimirArvore(){
if(this.root == null)
System.out.println("Árvore vazia");
else
imprimirArvore(this.root);
}
private void imprimirArvore(Node node){
if(node.getNoEsquerda() != null){
imprimirArvore(node.getNoEsquerda());
}
if (node.getNoDireita() != null){
imprimirArvore(node.getNoDireita());
}
System.out.println("Nó: " + node.getValor());
}
public void inserir(int valor){
inserir(this.root, valor);
}
public void inserir(Node node, int valor) {
if(this.root == null){
this.root = new Node(valor);
} else {
if (valor < node.getValor()) {
if (node.getNoEsquerda() != null) {
inserir(node.getNoEsquerda(), valor);
} else {
//Se nodo esquerdo vazio insere o novo no aqui
node.setNoEsquerda(new Node(valor));
}
//Verifica se o valor a ser inserido é maior que o no corrente da árvore, se sim vai para subarvore direita
} else if (valor > node.getValor()) {
//Se tiver elemento no no direito continua a busca
if (node.getNoDireita() != null) {
inserir(node.getNoDireita(), valor);
} else {
//Se nodo direito vazio insere o novo no aqui
node.setNoDireita(new Node(valor));
}
}
}
}
public Node remover(int valor) throws Exception{
return remover(this.root, valor);
}
private Node remover(Node node, int valor) throws Exception{
if(this.root == null){
throw new Exception("Árvore vazia");
} else {
if(valor < node.getValor()){
node.setNoEsquerda(remover(node.getNoEsquerda(), valor));
} else if(valor > node.getValor()){
node.setNoDireita(remover(node.getNoDireita(), valor));
} else if (node.getNoEsquerda() != null && node.getNoDireita() != null) {
/*2 filhos*/
System.out.println(" Removeu No " + node.getValor());
node.setValor(encontraMinimo(node.getNoDireita()).getValor());
node.setNoDireita(removeMinimo(node.getNoDireita()));
} else {
System.out.println(" Removeu No " + node.getValor());
node = (node.getNoEsquerda() != null) ? node.getNoEsquerda() : node.getNoDireita();
}
return node;
}
}
private Node removeMinimo(Node node) {
if (node == null) {
System.out.println(" ERRO ");
} else if (node.getNoEsquerda() != null) {
node.setNoEsquerda(removeMinimo(node.getNoEsquerda()));
return node;
} else {
return node.getNoDireita();
}
return null;
}
private Node encontraMinimo(Node node) {
if (node != null) {
while (node.getNoEsquerda() != null) {
node = node.getNoEsquerda();
}
}
return node;
}
}
- Começo a percorrer a árvore pela raiz
- Avalio se o valor que está sendo inserido é maior ou menor (não pode ser igual) do que o nó que estou avaliando.
- Caso seja menor, avalio se já existe um nó na esquerda e repito o passo 2.
- Caso não haja um nó esquerdo, crio um e gravo o novo valor ali.
- Caso o valor seja maior do que o nó que estou avaliando, avalio se já existe um nó na direita e repito o passo 2.
- Caso não haja um nó direito, crio um e gravo o novo valor ali.
- Avalio se a raíz é null. Caso seja informo que a árvore está vazia.
- Caso a raíz não seja null. Avalio se o valor que quero remover é maior ou menor que o nó que estou avaliando (o primeiro nó é a raíz, obviamente).
- Caso seja menor, acesso o nó esquerdo e faço isso até encontrá-lo.
- Quando encontro ele, avalio se ele tem filhos à esquerda e à direita.
- Caso tenha, vou para a sub-árvore da direita e procuro o nó de menor valor à esquerda. Depois de encontrá-lo, atribuo o valor desse menor nó ao nó que está sendo excluído (ver imagem 5).
- Depois da atribuição do passo 5 ter sido feita, eu preciso refazer a ligação que o pai do menor nó à esquerda tinha pois, agora o pai dele vai apontar para null.
- O valor passado como parâmetro foi excluído.
- Ao contrário do passo 3, ou seja, o valor é maior, acesso o nó direito e faço isso até encontrá-lo.
- Repito os passos 4 à 7.
- Caso um nó seja folha ou só tenha 1 filho, removo seu valor e atualizo seu pai.
20 comentários:
Cara muito obrigado pelo código e pelo post!!
Tive que fazer uma apresentação sobre árvore avl na facu, e seu blog me salvou!
A única coisa que tive que mudar foi o código, pois precisava da árvore balanceada, e a sua não faz as operações de rotação.
Porem a parte que ensina como funciona cada método ajudou muito!!
Pra quem quiser um código que faz as operações de balanceamentos vou deixar o link com o projeto que usei.
https://www.dropbox.com/s/7uam2tcpge8u0qj/Tree.zip?dl=0
Edugunz, quem agradece sou eu. Você não tem idéia de como eu fico feliz em saber que ajudei alguém e essa pessoa comentou no meu post. Você me fez ganhar o dia. Se quiser, sinta-se a vontade para sugerir temas.
:D
Preciso Estudar Sempre
Esqueci de perguntar algumas coisas:
- O texto estava bem escrito ?
- Você gostou do blog ?
- O projeto de exemplo estava bem codificado e funcionou ?
- Você entendeu o eu quis explicar ?
Haha valeu mesmo assim.
Sobre as perguntas, o texto ta bem escrito, linguagem fácil e descontraida. A iniciativa do blog é ótima, gostei sim.
O projeto do exemplo funciona certinho, só falta os métodos de rotação, mas pelo oque vc explicou essa parte, não era a principal.
Só pra adicionar mais uma coisa, eu fiz uma pequena apresentação sobre a Árvore AVL, só para introduzir antes do código. https://www.dropbox.com/s/x3dl1aeidk9ync7/Arvore.ppt?dl=0
Posso dar uma olhada nessa apresentação visando um novo post. Você tem alguma sugestão de tema ?
E para percorrer em pré ordem e in-ordem como vc implementaria ?
Ótima postagem, me ajudou muito. Vlw!
Allan Kelven, para percorrer a árvore dessas formas você tem que mudar a ordem em que os nós são visitados. Da forma que implementei, primeiro é visitado o nó da esquerda e depois da direita.
Para que você possa entender os tipos de ordem, vou deixar aqui um vídeo (usei como referência) que explicam as formas. Caso precise de mais ajuda, pode contar comigo.
[ED] Aula 73 - Percorrendo uma Árvore Binária - https://www.youtube.com/watch?v=z7XwVVYQRAA
Obrigado pela força Vinícius.
Gostaria de ver referências em Java já que em C complica o entendimento.
Muito bom o blog.
Jorge Higor, me fala quais foram suas dúvidas. A implementação que fiz é a mesma que foi feita em C, só que eu fiz em Java.
Conte comigo.
Olá gostei muito deste post, você sabe me dizer se tem algum post demonstrando como deve ser uma estrutura de dados para ser utilizado com essa estrutura de arvore binária? Preciso implementar uma estrutura como esta fazendo inclusive a gravação na base de dados. Grato.
Olá Roberto, obrigado pelo comentário. É sempre bom saber que o conteúdo que eu monto aqui atinge as pessoas. Em relação a sua dúvida, não entendi muito bem.
Você quer saber aonde você incorpora sua árvore binária ?
Como deve ser a modelagem do banco ?
Me explique melhor a sua situação para que possamos debater melhor. Caso você queira me mandar um email, escreva para precisoestudarsempre@gmail.com
Abraços e boa sorte !!
Muito obrigada de verdade, um amigo me mandou seu blog. Sua escrita é muito direta e objetiva. Valeu pelo código.
Caramba !! Tal comentário me deixa emocionado e muito feliz por saber que fiz diferença em seu estudo. Espero sempre agregar com um bom conteúdo e uma boa escrita.
Sinta-se a vontade para navegar entre os posts e estudar eles. Caso encontre algo que queira discutir, aqui é o lugar.
O blog possui canais oficiais de comunicação como o e-mail precisoestudarsempre@gmail.com e a página no facebook https://www.facebook.com/precisoestudarsempre/.
Se inscreva aqui no blog para receber as notificações de novos posts.
Estamos em 2017 e essa postagem me ajudou demais, muito obrigado pelas explicações, muito esclarecedor!
Muito obrigado pelo comentário. Fico feliz por você ter gostado do texto. :D
Bom dia. Estou estudando arvores na facul, e estou com dificuldades em uma arvore onde preciso achar os pares e os impares dela, e contar o numero de nos.
Em que ponto especificamente você está com dificuldades?
2023 e ainda salvando a vida dos estudantes!!
Postar um comentário