平衡是一种真正微妙的属性;你认为你知道它是什么,但它很容易出错。特别是,即使是埃里克·利珀特(Eric Lippert)的(好)答案也是不对的。那是因为身高的概念是不够的。您需要具有树的最小和最大高度的概念(其中最小高度是从根到叶子的最小步数,最大值是...好吧,你明白了)。鉴于此,我们可以将平衡定义为:
任何分支的最大高度不超过任何分支的最小高度的一个树。
(这实际上意味着分支本身是平衡的;您可以选择相同的分支作为最大值和最小值。
验证此属性所需要做的就是对当前深度进行简单的树遍历跟踪。第一次回溯时,这为您提供了基线深度。之后的每次回溯时,您都会将新深度与基线进行比较
- 如果它等于基线,那么你只需继续
- 如果有多个不同,则树不平衡
- 如果它是一次性的,那么您现在知道平衡的范围,并且所有后续深度(当您即将回溯时)必须是第一个或第二个值。
在代码中:
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;
}
});
}
}
我想你可以在不使用观察者模式的情况下做到这一点,但我发现以这种方式推理更容易。
[编辑]:为什么你不能只拿每边的高度。请考虑以下树:
/\
/ \
/ \
/ \_____
/\ / \_
/ \ / / \
/\ C /\ / \
/ \ / \ /\ /\
A B D E F G H J
好吧,有点乱,但根的每一边都是平衡的:深度为2,,,,是深度3,和,,,是深度4。左分支的高度为 2(请记住,当您遍历分支时,高度会减小),右分支的高度为 3。然而,整个树并不平衡,因为 和 之间的高度差为 2。您需要一个最小值规范(尽管实际算法可能不太复杂,因为应该只有两个允许的高度)。C
A
B
D
E
F
G
H
J
C
F