Como determinar se uma árvore binária está equilibrada
1. Visão geral
As árvores são uma das estruturas de dados mais importantes da ciência da computação. We’re usually interested in a balanced tree, because of its valuable properties. Sua estrutura permite realizar operações como consultas, inserções e exclusões em tempo logarítmico.
Neste tutorial, aprenderemos como determinar se uma árvore binária está balanceada.
2. Definições
Primeiro, vamos apresentar algumas definições para ter certeza de que estamos na mesma página:
-
A binary tree - um tipo de árvore onde cada nó tem zero, um ou dois filhos
-
Altura de uma árvore - uma distância máxima de uma raiz a uma folha (igual à profundidade da folha mais profunda)
-
Uma árvore balanceada - um tipo de árvore ondefor every subtree the maximum distance from the root to any leaf is at most bigger by one than the minimum distance from the root to any leaf
Podemos encontrar um exemplo de uma árvore binária equilibrada abaixo. Três arestas verdes são uma visualização simples de como determinar a altura, enquanto os números indicam o nível.
3. Objetos de domínio
Então, vamos começar com uma aula para nossa árvore:
public class Tree {
private int value;
private Tree left;
private Tree right;
public Tree(int value, Tree left, Tree right) {
this.value = value;
this.left = left;
this.right = right;
}
}
Por razões de simplicidade, digamoseach node has an integer value. Observe queif left and right trees are null, then it means our node is a leaf.
Antes de apresentarmos nosso método principal, vamos ver o que ele deve retornar:
private class Result {
private boolean isBalanced;
private int height;
private Result(boolean isBalanced, int height) {
this.isBalanced = isBalanced;
this.height = height;
}
}
Assim, para cada chamada, teremos informações sobre altura e equilíbrio.
4. Algoritmo
Tendo uma definição de uma árvore equilibrada, podemos criar um algoritmo. What we need to do is to check the desired property for every node. Isso pode ser conseguido facilmente com a busca recursiva em profundidade, pela primeira vez.
Agora, nosso método recursivo será chamado para cada nó. Além disso, ele acompanhará a profundidade atual. Cada chamada retornará informações sobre altura e equilíbrio.
Agora, vamos dar uma olhada em nosso método em profundidade:
private Result isBalancedRecursive(Tree tree, int depth) {
if (tree == null) {
return new Result(true, -1);
}
Result leftSubtreeResult = isBalancedRecursive(tree.left(), depth + 1);
Result rightSubtreeResult = isBalancedRecursive(tree.right(), depth + 1);
boolean isBalanced = Math.abs(leftSubtreeResult.height - rightSubtreeResult.height) <= 1;
boolean subtreesAreBalanced = leftSubtreeResult.isBalanced && rightSubtreeResult.isBalanced;
int height = Math.max(leftSubtreeResult.height, rightSubtreeResult.height) + 1;
return new Result(isBalanced && subtreesAreBalanced, height);
}
Primeiro, precisamos considerar o caso se nosso nó fornull: retornaremostrue (o que significa que a árvore está equilibrada) e-1 como uma altura.
Então,we make two recursive calls for the left and the right subtree, keeping the depth up to date.
Neste ponto, temos cálculos realizados para filhos de um nó atual. Agora, temos todos os dados necessários para verificar o saldo:
-
a variávelisBalanced verifica a altura das crianças, e
-
substreesAreBalanced indica se as subárvores também estiverem balanceadas
Finalmente, podemos retornar informações sobre equilíbrio e altura. Também pode ser uma boa ideia simplificar a primeira chamada recursiva com um método de fachada:
public boolean isBalanced(Tree tree) {
return isBalancedRecursive(tree, -1).isBalanced;
}
5. Sumário
Neste artigo, discutimos como determinar se uma árvore binária está balanceada. Explicamos uma abordagem de pesquisa em profundidade.
Como de costume, o código-fonte com os testes está disponívelover on GitHub.