그 학창 시절부터 오래되었습니다. 병원에서 IT 전문가로 취직했습니다. 지금 실제 프로그래밍을하기 위해 이동하려고합니다. 나는 지금 이진 트리에 대해 작업하고 있는데, 트리가 높이 균형을 이루는 지 결정하는 가장 좋은 방법이 무엇인지 궁금합니다.
나는 이것과 함께 무언가를 생각하고 있었다.
public boolean isBalanced(Node root){
if(root==null){
return true; //tree is empty
}
else{
int lh = root.left.height();
int rh = root.right.height();
if(lh - rh > 1 || rh - lh > 1){
return false;
}
}
return true;
}
이것이 좋은 구현입니까? 아니면 내가 뭔가를 놓치고 있습니까?
답변
다른 것을 검색하는 동안이 오래된 질문을 우연히 발견했습니다. 나는 당신이 완전한 답을 얻지 못했다는 것을 알았습니다.
이 문제를 해결하는 방법은 작성하려는 함수에 대한 사양을 작성하여 시작하는 것입니다.
사양 : 잘 구성된 이진 트리는 (1) 비어 있거나 (2) 왼쪽 및 오른쪽 자식이 높이 균형을 이루고 왼쪽 트리의 높이가 1 사이에있는 경우 “높이 균형”이라고합니다. 오른쪽 나무의 높이.
이제 사양이 준비되었으므로 코드를 작성하는 것은 간단합니다. 사양을 따르십시오.
IsHeightBalanced(tree)
return (tree is empty) or
(IsHeightBalanced(tree.left) and
IsHeightBalanced(tree.right) and
abs(Height(tree.left) - Height(tree.right)) <= 1)
선택한 프로그래밍 언어로 번역하는 것은 간단합니다.
보너스 연습 :이 순진한 코드 스케치는 높이를 계산할 때 나무를 너무 많이 횡단합니다. 더 효율적으로 만들 수 있습니까?
슈퍼 보너스 운동 : 나무가 엄청나게 불균형 하다고 가정하십시오 . 한쪽에는 백만 개의 노드가 있고 다른쪽에는 세 개의 노드가 있습니다. 이 알고리즘이 스택을 파괴하는 시나리오가 있습니까? 엄청나게 불균형 한 트리가 주어 졌을 때에도 스택이 날아 가지 않도록 구현을 수정할 수 있습니까?
최신 정보 : Donal Fellows는 자신의 답변에서 선택할 수있는 ‘균형’에 대한 다른 정의가 있다고 지적합니다. 예를 들어, “높이 균형”에 대한보다 엄격한 정의를 취하고 가장 가까운 빈 자식에 대한 경로 길이가 가장 먼 빈 자식 에 대한 경로 중 하나 내에 있어야합니다 . 내 정의는 그보다 덜 엄격하므로 더 많은 나무를 인정합니다.
하나는 내 정의보다 덜 엄격 할 수도 있습니다. 균형 잡힌 트리는 각 가지에서 빈 트리까지의 최대 경로 길이가 2, 3 또는 다른 상수만큼 차이가 나는 트리라고 말할 수 있습니다. 또는 최대 경로 길이가 최소 경로 길이의 일부 (예 : 1/2 또는 1/4)입니다.
보통은 정말 중요하지 않습니다. 트리 밸런싱 알고리즘의 요점은 한쪽에 백만 개의 노드가 있고 다른쪽에 세 개가있는 상황에 처하지 않도록하는 것입니다. Donal의 정의는 이론적으로는 괜찮지 만 실제로는 그 엄격함 수준을 충족하는 트리 균형 알고리즘을 만드는 것이 고통입니다. 성능 절감은 일반적으로 구현 비용을 정당화하지 않습니다. 실제로는 거의 차이가없는 균형 수준을 얻기 위해 불필요한 트리 재 배열을하는 데 많은 시간을 할애합니다. 이론적으로 완벽하게 균형 잡힌 나무에서 20 개만 걸릴 수 있는데 백만 개의 노드 불완전하게 균형이 잡힌 나무에서 가장 먼 잎에 도달하는 데 때때로 40 개의 가지가 필요한데 누가 신경 쓰겠습니까? 요점은 백만 달러가 걸리지 않는다는 것입니다. 최악의 경우 100 만에서 최악의 경우 40으로 줄이면 보통 충분합니다. 최적의 경우로 끝까지 갈 필요가 없습니다.
답변
균형은 정말 미묘한 속성입니다. 당신은 그것이 무엇인지 알고 있다고 생각하지만 잘못되기가 너무 쉽습니다. 특히 Eric Lippert의 (좋은) 대답조차도 꺼져 있습니다. 높이 의 개념이 충분하지 않기 때문 입니다. 나무의 최소 및 최대 높이의 개념이 필요합니다 (최소 높이는 뿌리에서 잎까지의 최소 단계 수이고 최대 값은 … 음, 그림을 얻습니다). 이를 고려하여 균형을 다음과 같이 정의 할 수 있습니다.
나뭇 가지의 최대 높이가 나뭇 가지 의 최소 높이 보다 1 배 이하인 나무.
(이것은 실제로 분기 자체가 균형을 이루고 있음을 의미합니다. 최대 및 최소 모두에 대해 동일한 분기를 선택할 수 있습니다.)
이 속성을 확인하기 위해 필요한 것은 현재 깊이를 추적하는 간단한 트리 순회뿐입니다. 처음으로 역 추적하면 기준 깊이가 제공됩니다. 그 후 역 추적 할 때마다 새 깊이를 기준선과 비교합니다.
- 기준선과 같으면 계속
- 둘 이상 다르면 나무가 균형을 이루지 못합니다.
- 일회성 인 경우 이제 균형 범위를 알고 있으며 모든 후속 깊이 (역 추적하려는 경우)는 첫 번째 또는 두 번째 값이어야합니다.
코드에서 :
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;
}
});
}
}
Observer 패턴을 사용하지 않고도이 작업을 수행 할 수 있다고 생각하지만 이런 식으로 추론하는 것이 더 쉽습니다.
[편집] : 왜 당신은 각 측면의 높이를 취할 수 없습니다. 이 트리를 고려하십시오.
/\
/ \
/ \
/ \_____
/\ / \_
/ \ / / \
/\ C /\ / \
/ \ / \ /\ /\
A B D E F G H J
OK, 조금 지저분하지만 루트의 각 측면은 균형 : C
깊이 2되고 A
, B
, D
, E
깊이 3,있다가 F
, G
, H
, J
깊이 4. 왼쪽 지점의 높이입니다은 2 (높이 기억은 트래버스 당신으로 감소 분기) 오른쪽 지점의 높이가 3입니다 그러나 전체 트리입니다 하지 둘 사이의 높이 차이가로 균형 C
및F
. 최소 최대 사양이 필요합니다 (허용되는 높이가 두 개만 있어야하므로 실제 알고리즘이 덜 복잡 할 수 있음).
답변
이것은 트리의 최상위 레벨이 균형을 이루는 지 여부 만 결정합니다. 즉, 맨 왼쪽과 맨 오른쪽에 두 개의 긴 가지가 있고 가운데에 아무것도없는 나무가있을 수 있으며 이것은 true를 반환합니다. true를 반환하기 전에 root.left
및 을 재귀 적으로 확인하여 root.right
내부적으로 균형이 맞는지 확인해야 합니다.
답변
보너스 운동 반응. 간단한 해결책. 분명히 실제 구현에서는 사용자가 응답에 높이를 포함하도록 요구하지 않기 위해 이것 또는 무언가를 래핑 할 수 있습니다.
IsHeightBalanced(tree, out height)
if (tree is empty)
height = 0
return true
balance = IsHeightBalanced(tree.left, heightleft) and IsHeightBalanced(tree.right, heightright)
height = max(heightleft, heightright)+1
return balance and abs(heightleft - heightright) <= 1
답변
주문 후 솔루션, 트리를 한 번만 횡단합니다. 시간 복잡도는 O (n), 공간은 O (1), 하향식 솔루션보다 낫습니다. Java 버전 구현을 제공합니다.
public static <T> boolean isBalanced(TreeNode<T> root){
return checkBalance(root) != -1;
}
private static <T> int checkBalance(TreeNode<T> node){
if(node == null) return 0;
int left = checkBalance(node.getLeft());
if(left == -1) return -1;
int right = checkBalance(node.getRight());
if(right == -1) return -1;
if(Math.abs(left - right) > 1){
return -1;
}else{
return 1 + Math.max(left, right);
}
}
답변
높이 균형 이진 트리의 정의는 다음과 같습니다.
모든 노드 의 두 하위 트리 높이 가 1보다 크지 않은 이진 트리
따라서 빈 이진 트리는 항상 높이가 균형을 이룹니다.
비어 있지 않은 이진 트리는 다음과 같은 경우 높이 균형을 이룹니다.
- 왼쪽 하위 트리는 높이가 균형을 이룹니다.
- 오른쪽 하위 트리는 높이가 균형을 이룹니다.
- 왼쪽 및 오른쪽 하위 트리의 높이 차이는 1보다 크지 않습니다.
트리를 고려하십시오.
A
\
B
/ \
C D
보시다시피의 왼쪽 하위 트리는 A
높이가 균형을 이루며 (비어 있기 때문에) 오른쪽 하위 트리도 마찬가지입니다. 그러나 왼쪽 하위 트리의 0
높이가 오른쪽 하위 트리의 높이가이므로 조건 3이 충족되지 않으므로 트리는 높이 균형이 맞지 않습니다.2
.
또한 다음 트리는 왼쪽 및 오른쪽 하위 트리의 높이가 동일하더라도 높이 균형이 맞지 않습니다. 기존 코드는 이에 대해 true를 반환합니다.
A
/ \
B C
/ \
D G
/ \
E H
그래서 단어 마다 def의 는 매우 중요합니다.
이것은 작동합니다 :
int height(treeNodePtr root) {
return (!root) ? 0: 1 + MAX(height(root->left),height(root->right));
}
bool isHeightBalanced(treeNodePtr root) {
return (root == NULL) ||
(isHeightBalanced(root->left) &&
isHeightBalanced(root->right) &&
abs(height(root->left) - height(root->right)) <=1);
}
답변
바이너리 트리가 균형을 이루고 있거나 레벨 순서 순회로 확인할 수없는 경우 :
private boolean isLeaf(TreeNode root) {
if (root.left == null && root.right == null)
return true;
return false;
}
private boolean isBalanced(TreeNode root) {
if (root == null)
return true;
Vector<TreeNode> queue = new Vector<TreeNode>();
int level = 1, minLevel = Integer.MAX_VALUE, maxLevel = Integer.MIN_VALUE;
queue.add(root);
while (!queue.isEmpty()) {
int elementCount = queue.size();
while (elementCount > 0) {
TreeNode node = queue.remove(0);
if (isLeaf(node)) {
if (minLevel > level)
minLevel = level;
if (maxLevel < level)
maxLevel = level;
} else {
if (node.left != null)
queue.add(node.left);
if (node.right != null)
queue.add(node.right);
}
elementCount--;
}
if (abs(maxLevel - minLevel) > 1) {
return false;
}
level++;
}
return true;
}