L'equilibrio è una proprietà davvero sottile; pensi di sapere cos'è, ma è così facile sbagliare. In particolare, anche la (buona) risposta di Eric Lippert è spenta. Questo perché la nozione di altezza non è sufficiente. Devi avere il concetto di altezza minima e massima di un albero (dove l'altezza minima è il numero minimo di gradini dalla radice a una foglia, e il massimo è ... beh, ottieni l'immagine). Detto questo, possiamo definire l'equilibrio come:
Un albero in cui l'altezza massima di qualsiasi ramo non è più di uno in più dell'altezza minima di qualsiasi ramo.
(Questo in realtà implica che i rami siano essi stessi bilanciati; puoi scegliere lo stesso ramo sia per il massimo che per il minimo.)
Tutto quello che devi fare per verificare questa proprietà è un semplice attraversamento dell'albero che tiene traccia della profondità corrente. La prima volta che torni indietro, ottieni una profondità della linea di base. Dopo ogni volta che torni indietro, confronti la nuova profondità con la linea di base
- se è uguale alla linea di base, allora continui
- se è più di uno diverso, l'albero non è equilibrato
- se è una tantum, ora conosci l'intervallo per il bilanciamento e tutte le profondità successive (quando stai per tornare indietro) devono essere il primo o il secondo valore.
In codice:
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;
}
});
}
}
Suppongo che potresti farlo senza usare il pattern Observer, ma trovo più facile ragionare in questo modo.
[EDIT]: Perché non puoi semplicemente prendere l'altezza di ciascun lato. Considera questo albero:
/\
/ \
/ \
/ \_____
/\ / \_
/ \ / / \
/\ C /\ / \
/ \ / \ /\ /\
A B D E F G H J
OK, un po disordinato, ma ciascun lato della radice è bilanciato: C
è la profondità 2, A
, B
, D
, E
sono la profondità 3, e F
, G
, H
, J
sono profondità 4. L'altezza del ramo sinistro è 2 (ricordiamo l'altezza diminuisce mentre attraversate il ramo), l'altezza del ramo destro è 3. Tuttavia l'albero complessivo non è bilanciato in quanto vi è un dislivello di 2 tra C
e F
. È necessaria una specifica minimax (sebbene l'algoritmo effettivo possa essere meno complesso in quanto dovrebbero esserci solo due altezze consentite).