查找树是否平衡的迭代解决方案
Iterative Solution to finding if a tree is balanced
所以有人发布了他们的解决方案,但我发现它似乎不起作用,我将其发布在那里,但我想让其他人更容易访问它。
问题在"Cracking the Code Interview"中,是第一个树题,欢迎提出其他建议(或证明我错了!)
这里的关键是很难用一堆来跟踪最终路径及其高度。
我最后做的是将左右 child 的高度都推到堆栈上,检查它们是否在彼此的范围内,将 1 加到最大值,然后将其推到左右弹出后堆叠。
我已经发表评论了,希望它足够清楚
/* Returns true if binary tree with root as root is height-balanced */
boolean isBalanced(Node root) {
if(root == null) return false;
Deque<Integer> heights = new LinkedList<>();
Deque<Node> trail = new LinkedList<>();
trail.push(root);
Node prev = root; //set to root not null to not confuse when root is misisng children
while(!trail.isEmpty()) {
Node curr = trail.peek(); //get the next node to process, peek because we need to maintain trail until we return
//if we just returned from left child
if (curr.left == prev) {
if(curr.right != null) trail.push(curr.right); //if we can go right go
else {
heights.push(-1); //otherwise right height is -1 does not exist and combine heights
if(!combineHeights(heights)) return false;
trail.pop(); //back to parent
}
}
//if we just returned from right child
else if (curr.right == prev) {
if(!combineHeights(heights)) return false;
trail.pop(); //up to parent
}
//this came from a parent, first thing is to visit the left child, or right if no left
else {
if(curr.left != null) trail.push(curr.left);
else {
if (curr.right != null) {
heights.push(-1); //no left so when we combine this node left is 0
trail.push(curr.right); //since we never go left above logic does not go right, so we must here
}
else { //no children set height to 1
heights.push(0);
trail.pop(); //back to parent
}
}
}
prev = curr;
}
return true;
}
//pop both previous heights and make sure they are balanced, if not return false, if so return true and push the greater plus 1
private boolean combineHeights(Deque<Integer> heights) {
int rightHeight = heights.pop();
int leftHeight = heights.pop();
if(Math.abs(leftHeight - rightHeight) > 1) return false;
else heights.push(Math.max(leftHeight, rightHeight) + 1);
return true;
}
书上原题没有提到树是二叉的。我碰巧解决了同样的问题,但编码为 Python。因此,这是我针对一般树(节点的子节点存储在列表中)的问题的迭代解决方案,在 python.
中
def is_balanced_nonrecursive(self):
stack = [self.root]
levels = [0]
current_min = sys.maxint
current_max = 0
current_level = 0
while len(stack) > 0:
n = stack.pop()
current_level = levels.pop()
for c in n.children:
stack.append(c)
levels.append(current_level + 1)
if len(n.children) == 0:
if current_level < current_min:
current_min = current_level
if current_level > current_max:
current_max = current_level
return current_max - current_min < 2
这基本上是树的深度优先遍历。我们为关卡保留一个单独的堆栈(列表 levels
)。如果我们看到任何叶节点,我们会相应地更新当前的最小和当前最大级别。该算法遍历整棵树,最后如果 max 和 min levels 相差超过 1,则树不平衡。
有很多可能的优化,例如检查循环内的最小值和最大值的差值是否大于1,如果是,则立即return False
。
这个有一些代码重复,但至少它不会像递归代码那样让我头疼:
public boolean isBalanced() {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int leftLevel = 0;
int rightLevel = 0;
if(this == null) return false;
if(this.left != null)queue.offer(this.left);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
leftLevel++;
}
if(this.right != null) queue.offer(this.right);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
rightLevel++;
}
return Math.abs(leftLevel - rightLevel) < 2;
}
所以有人发布了他们的解决方案,但我发现它似乎不起作用,我将其发布在那里,但我想让其他人更容易访问它。
问题在"Cracking the Code Interview"中,是第一个树题,欢迎提出其他建议(或证明我错了!)
这里的关键是很难用一堆来跟踪最终路径及其高度。
我最后做的是将左右 child 的高度都推到堆栈上,检查它们是否在彼此的范围内,将 1 加到最大值,然后将其推到左右弹出后堆叠。
我已经发表评论了,希望它足够清楚
/* Returns true if binary tree with root as root is height-balanced */
boolean isBalanced(Node root) {
if(root == null) return false;
Deque<Integer> heights = new LinkedList<>();
Deque<Node> trail = new LinkedList<>();
trail.push(root);
Node prev = root; //set to root not null to not confuse when root is misisng children
while(!trail.isEmpty()) {
Node curr = trail.peek(); //get the next node to process, peek because we need to maintain trail until we return
//if we just returned from left child
if (curr.left == prev) {
if(curr.right != null) trail.push(curr.right); //if we can go right go
else {
heights.push(-1); //otherwise right height is -1 does not exist and combine heights
if(!combineHeights(heights)) return false;
trail.pop(); //back to parent
}
}
//if we just returned from right child
else if (curr.right == prev) {
if(!combineHeights(heights)) return false;
trail.pop(); //up to parent
}
//this came from a parent, first thing is to visit the left child, or right if no left
else {
if(curr.left != null) trail.push(curr.left);
else {
if (curr.right != null) {
heights.push(-1); //no left so when we combine this node left is 0
trail.push(curr.right); //since we never go left above logic does not go right, so we must here
}
else { //no children set height to 1
heights.push(0);
trail.pop(); //back to parent
}
}
}
prev = curr;
}
return true;
}
//pop both previous heights and make sure they are balanced, if not return false, if so return true and push the greater plus 1
private boolean combineHeights(Deque<Integer> heights) {
int rightHeight = heights.pop();
int leftHeight = heights.pop();
if(Math.abs(leftHeight - rightHeight) > 1) return false;
else heights.push(Math.max(leftHeight, rightHeight) + 1);
return true;
}
书上原题没有提到树是二叉的。我碰巧解决了同样的问题,但编码为 Python。因此,这是我针对一般树(节点的子节点存储在列表中)的问题的迭代解决方案,在 python.
中def is_balanced_nonrecursive(self):
stack = [self.root]
levels = [0]
current_min = sys.maxint
current_max = 0
current_level = 0
while len(stack) > 0:
n = stack.pop()
current_level = levels.pop()
for c in n.children:
stack.append(c)
levels.append(current_level + 1)
if len(n.children) == 0:
if current_level < current_min:
current_min = current_level
if current_level > current_max:
current_max = current_level
return current_max - current_min < 2
这基本上是树的深度优先遍历。我们为关卡保留一个单独的堆栈(列表 levels
)。如果我们看到任何叶节点,我们会相应地更新当前的最小和当前最大级别。该算法遍历整棵树,最后如果 max 和 min levels 相差超过 1,则树不平衡。
有很多可能的优化,例如检查循环内的最小值和最大值的差值是否大于1,如果是,则立即return False
。
这个有一些代码重复,但至少它不会像递归代码那样让我头疼:
public boolean isBalanced() {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int leftLevel = 0;
int rightLevel = 0;
if(this == null) return false;
if(this.left != null)queue.offer(this.left);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
leftLevel++;
}
if(this.right != null) queue.offer(this.right);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i < size; i++) {
if(queue.peek().left != null) queue.offer(queue.peek().left);
if(queue.peek().right != null) queue.offer(queue.peek().right);
queue.poll();
}
rightLevel++;
}
return Math.abs(leftLevel - rightLevel) < 2;
}