如何平衡一棵大型AVL树?

huangapple go评论81阅读模式
英文:

How do I balance a large AVL tree?

问题

我正在尝试重新学习如何平衡AVL树。在观看了许多教程之后,我以为我已经掌握了要领,但是我遇到了一个特殊情况,现在我陷入了困境。

我正在使用数字 5, 2, 3, 9, 4, 1, 6, 8, 10, 7 按顺序构建一个AVL树。在添加10之前,树可以平衡,没有任何问题,但是在添加7之后,我的算法无法平衡树。在我平衡添加7之前,树的样子如下:

    3
  ┌─┴──┐
  2    8 <-- 不平衡的子树
┌─┘  ┌─┴──┐
1    5    9
    ┌┴─┐  └─┐
    4  6   10
       └─┐
         7 <-- 导致树不平衡的节点

我从使用USFCA的AVL树可视化工具得知,我平衡后的树应该是这样的,但我不知道如何达到这个结果:

      5
   ┌──┴───┐
   3      8
  ┌┴─┐  ┌─┴──┐
  2  4  6    9
┌─┘     └─┐  └─┐
1         7   10

这个教程指示我应该对8进行左右旋转,但结果与期望的结果不一致:

     8
   ┌─┴──┐
   3    9
  ┌┴─┐  └─┐
  2  4   10
┌─┘  └─┐
1      5
       └─┐
         6
         └─┐
           7

这是我用来确定给定节点应该使用哪种旋转的逻辑(如果需要,告诉我是否应该包含我的旋转方法的代码 - 我不确定是否有必要提供一个最小可复现示例):

private void balance(Node node) {
	if (node == null)
		return;

	int balanceFactor = balanceFactor(node);
	if (balanceFactor > 1) {
        // 左子树更重
		balanceFactor = balanceFactor(node.left);
		if (balanceFactor > 0) {
			rightRotate(node.left.left);
		} else {
			leftRotate(node.left.right);
			rightRotate(node.left);
		}
	} else if (balanceFactor < -1) {
        // 右子树更重
		balanceFactor = balanceFactor(node.right);
		if (balanceFactor > 0) {
			leftRotate(node.right);
			rightRotate(node.right.left);
		} else {
			leftRotate(node.right);
		}
	}

	balance(node.parent);
}

在根节点变得右重时,这个逻辑似乎工作得很好。

我需要做什么不同的来平衡这棵树呢?

英文:

I am trying to relearn how to balance AVL trees. After watching many tutorials, I thought I got the gist of it, but then I came across a particular scenario and now I'm stumped.

I am building an AVL tree using the numbers 5, 2, 3, 9, 4, 1, 6, 8, 10, 7, inserted in that order. The tree balances without any issues all the way up to 10, but my algorithm cannot balance the tree after adding 7. Here is what the tree looks like before I balance it after adding 7:

    3
  ┌─┴──┐
  2    8 <-- Unbalanced subtree
┌─┘  ┌─┴──┐
1    5    9
    ┌┴─┐  └─┐
    4  6   10
       └─┐
         7 <-- Node causing the tree to be unbalanced 

I know from using USFCA's AVL Tree Visualizer that my tree is suppose to look like this after I balance it, but I do not know how to achieve this result:

      5
   ┌──┴───┐
   3      8
  ┌┴─┐  ┌─┴──┐
  2  4  6    9
┌─┘     └─┐  └─┐
1         7   10

This tutorial indicates that I should perform a left-right rotation on 8, which results in something inconsistent with the desired result:

     8
   ┌─┴──┐
   3    9
  ┌┴─┐  └─┐
  2  4   10
┌─┘  └─┐
1      5
       └─┐
         6
         └─┐
           7

This is the logic I am using to determine which rotations to use for a given node (let me know if I should include the code for my rotate methods - I am not sure if it is necessary for an MCVE):

private void balance(Node node) {
	if (node == null)
		return;

	int balanceFactor = balanceFactor(node);
	if (balanceFactor > 1) {
        // Left subtree is heavier
		balanceFactor = balanceFactor(node.left);
		if (balanceFactor > 0) {
			rightRotate(node.left.left);
		} else {
			leftRotate(node.left.right);
			rightRotate(node.left);
		}
	} else if (balanceFactor < -1) {
        // Right subtree is heavier
		balanceFactor = balanceFactor(node.right);
		if (balanceFactor > 0) {
			leftRotate(node.right);
			rightRotate(node.right.left);
		} else {
			leftRotate(node.right);
		}
	}

	balance(node.parent);
}

It would seem to me that the logic works well until the root node becomes right-heavy.

What do I need to do differently to balance this tree?

答案1

得分: 2

balanceFactor < -1时的代码块应该与另一个代码块在左右和平衡因子比较方面相反。目前情况并非如此。所以将其更改为:

} else if (balanceFactor < -1) {
    // 右子树更重
    balanceFactor = balanceFactor(node.right);
    if (balanceFactor <= 0) {
        leftRotate(node.right.right);
    } else {
        rightRotate(node.right.left);
        leftRotate(node.right);
    }
}

请注意,我使用了<=。在第一个情况(>=)中也可以这样做,因为即使子节点的平衡因子为零,你也可以只进行一次旋转。

另请参阅Wikipedia上的重新平衡。请注意,他们对于平衡因子使用了相反的符号。

英文:

The code block for the case where balanceFactor &lt; -1 should mirror the other block in terms of left/right and balance factor comparisons. This is currently not the case. So change it to:

} else if (balanceFactor &lt; -1) {
    // Right subtree is heavier
    balanceFactor = balanceFactor(node.right);
    if (balanceFactor &lt;= 0) {
        leftRotate(node.right.right);
    } else {
        rightRotate(node.right.left);
        leftRotate(node.right);
    }
}

Note that I used &lt;=. This can also be done in the first case (&gt;=), as even when the balance factor at the child is zero, you can suffice with one rotation.

See also Wikipedia on rebalancing. Be aware that they use an opposite sign for the balance factor.

答案2

得分: 0

根据wikipedia的说明,你需要进行双旋转,因为节点8的内部子树是最长的。

所以你有以下树结构:

       3
 ...         8
          5     ...
       最长的子树

经过双旋转后,节点5的子节点分别为38,而节点5的右子树成为节点3的左子树,节点5的左子树成为节点8的右子树。得到的树结构如下:

         5
    3          8
  2   4    6     9
1            7     10

成功!经过双旋转后,树已经达到AVL平衡。

英文:

According to wikipedia you have to use a double rotation because the inner subtree of 8 is the longest.

So you have

       3
 ...         8
          5     ...
       longest

After the double rotation, 5 have resp. 3 and 8 for its children, and the
right child branch of 5 becomes the left child branch of 3, and the same the left child branch of 5 becomes the right child branch of 8. Which gives:

         5
    3          8
  2   4    6     9
1            7     10

Win! After the double rotation the tree is AVL balanced....

huangapple
  • 本文由 发表于 2023年8月8日 22:40:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76860633.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定