O equilíbrio é uma propriedade realmente sutil; você acha que sabe o que é, mas é tão fácil errar. Em particular, até a resposta (boa) de Eric Lippert está errada. Isso porque a noção de altura não basta. Você precisa ter o conceito de alturas mínimas e máximas de uma árvore (onde a altura mínima é o menor número de passos da raiz à folha, e o máximo é ... bem, você entendeu). Dado isso, podemos definir o equilíbrio como:
Uma árvore onde a altura máxima de qualquer galho não é mais do que um a mais do que a altura mínima de qualquer galho.
(Na verdade, isso implica que os ramos estão equilibrados; você pode escolher o mesmo ramo para máximo e mínimo.)
Tudo o que você precisa fazer para verificar essa propriedade é uma simples travessia da árvore mantendo o controle da profundidade atual. A primeira vez que você volta atrás, isso lhe dá uma profundidade de linha de base. Cada vez depois disso, quando você voltar atrás, você compara a nova profundidade com a linha de base
- se for igual à linha de base, você simplesmente continua
- se for mais de um diferente, a árvore não está equilibrada
- se for um, agora você conhece a faixa de equilíbrio e todas as profundidades subsequentes (quando você está prestes a retroceder) devem ser o primeiro ou o segundo valor.
Em código:
class Tree {
Tree left, right;
static interface Observer {
public void before();
public void after();
public boolean end();
}
static boolean traverse(Tree t, Observer o) {
if (t == null) {
return o.end();
} else {
o.before();
try {
if (traverse(left, o))
return traverse(right, o);
return false;
} finally {
o.after();
}
}
}
boolean balanced() {
final Integer[] heights = new Integer[2];
return traverse(this, new Observer() {
int h;
public void before() { h++; }
public void after() { h--; }
public boolean end() {
if (heights[0] == null) {
heights[0] = h;
} else if (Math.abs(heights[0] - h) > 1) {
return false;
} else if (heights[0] != h) {
if (heights[1] == null) {
heights[1] = h;
} else if (heights[1] != h) {
return false;
}
}
return true;
}
});
}
}
Suponho que você possa fazer isso sem usar o padrão Observer, mas acho mais fácil raciocinar dessa forma.
[EDIT]: Por que você não pode simplesmente medir a altura de cada lado. Considere esta árvore:
/\
/ \
/ \
/ \_____
/\ / \_
/ \ / / \
/\ C /\ / \
/ \ / \ /\ /\
A B D E F G H J
OK, um confuso pouco, mas cada lado da raiz é equilibrado: C
é profundidade 2, A
, B
, D
, E
estão profundidade 3, e F
, G
, H
, J
estão profundidade 4. A altura do ramo esquerdo é 2 (lembre-se a altura diminui à medida que atravessa o galho), a altura do galho direito é 3. No entanto, a árvore em geral não está equilibrada, pois há uma diferença de altura de 2 entre C
e F
. Você precisa de uma especificação minimax (embora o algoritmo real possa ser menos complexo, pois deve haver apenas duas alturas permitidas).