Cân bằng là một tài sản thực sự tinh tế; bạn nghĩ rằng bạn biết nó là gì, nhưng nó rất dễ sai. Đặc biệt, ngay cả câu trả lời (tốt) của Eric Lippert cũng bị tắt. Đó là bởi vì khái niệm về chiều cao là không đủ. Bạn cần phải có khái niệm về chiều cao tối thiểu và tối đa của cây (trong đó chiều cao tối thiểu là số bậc từ gốc đến lá ít nhất và tối đa là ... tốt, bạn sẽ có được hình ảnh). Do đó, chúng ta có thể định nghĩa số dư là:
Một cây nơi chiều cao tối đa của bất kỳ chi nhánh là không nhiều hơn một hơn chiều cao tối thiểu của bất kỳ chi nhánh.
(Điều này thực sự ngụ ý rằng các nhánh tự cân bằng; bạn có thể chọn cùng một nhánh cho cả tối đa và tối thiểu.)
Tất cả những gì bạn cần làm để xác minh thuộc tính này là theo dõi đường ngang cây đơn giản về độ sâu hiện tại. Lần đầu tiên bạn quay lại, điều đó cung cấp cho bạn độ sâu cơ bản. Mỗi lần sau đó khi quay lại, bạn so sánh độ sâu mới với đường cơ sở
- nếu nó bằng với đường cơ sở, thì bạn chỉ cần tiếp tục
- nếu nó khác nhiều hơn một, cây không cân đối
- nếu nó bị tắt một lần, thì bây giờ bạn biết phạm vi cho sự cân bằng và tất cả các độ sâu tiếp theo (khi bạn chuẩn bị lùi lại) phải là giá trị đầu tiên hoặc giá trị thứ hai.
Trong mã:
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;
}
});
}
}
Tôi cho rằng bạn có thể làm điều này mà không cần sử dụng mẫu Observer, nhưng tôi thấy lý do theo cách này dễ dàng hơn.
[EDIT]: Tại sao bạn không thể chỉ lấy chiều cao của mỗi cạnh. Hãy xem xét cây này:
/\
/ \
/ \
/ \_____
/\ / \_
/ \ / / \
/\ C /\ / \
/ \ / \ /\ /\
A B D E F G H J
OK, một chút lộn xộn, nhưng mỗi bên của gốc được cân bằng: C
là độ sâu 2, A
, B
, D
, E
là độ sâu 3, và F
, G
, H
, J
là chiều sâu 4. Chiều cao của chi nhánh bên trái là 2 (nhớ chiều cao giảm khi bạn đi ngang nhánh), chiều cao của nhánh phải là 3. Tuy nhiên, tổng thể cây không cân đối vì có sự khác biệt về chiều cao 2 giữa C
và F
. Bạn cần một đặc tả minimax (mặc dù thuật toán thực tế có thể ít phức tạp hơn vì chỉ nên có hai độ cao được phép).