一、问题引入
你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在、
上边BST 存在的问题分析:
二、平衡二叉树的概念
上图中1、2都是平衡二叉树,而3不是。
那我们如何解决不平衡问题呢?采用左旋转、右旋转、双旋转(左+右或右+左)的方式
三、平衡二叉树的操作
1.查询树、左右子树的高度
利用递归的思想,非常巧妙
//1.获取子树高度
public int Height(Node node) {
return Math.max(node.left == null ? 0 : Height(node.left), node.right == null ? 0 : Height(node.right)) + 1;
}
//2.获取左子树高度
public int leftHeight(Node node) {
if (node.left == null) return 0;
else return Height(node.left);
}
//3.获取右子树高度
public int rightHeight(Node node) {
if (node.right == null) return 0;
else return Height(node.right);
}
2.左旋转
思路,以下图为例,以 “4” 节点为基准进行左旋转:
1.先创建一个新的 “4” 节点 new,新的 “4” 称为“new”,旧的“4”称为“old。
2.new 的左子树设为 old 的左子树。"4"->"3"
2.new的右子树设为 old 右子树的左子树 "4"->"5"
至此,新节点的左右子树都设置好了。
3. old的值换成其右子节点的值 ”4“=”6“
4.old的右子树设置为其右子树的右子树 "6"-> "7"
5.old的左子树设置为new "6"->"4"
至此,旧节点的左右子树都设置好了。
整个过程相当于把根节点的右节点当做新的根节点,把它向上拎了一下。
我们创建了一个新的 "4" 节点,它原来的位置被它的右子树 “6” 替换了,而 "6" 原来的位置相当于被废弃掉了,不会再访问到。
//4.左旋转
public void leftRotate(Node root) {
Node newNode = new Node(root.value);
newNode.left = root.left;
newNode.right = root.right.left;
root.value = root.right.value;
root.right = root.right.right;
root.left = newNode;
}
测试:
@Test
public void testLeftRotate() {
int[] arr = {4, 3, 6, 5, 7, 8};
Node root = new Node(4);
BalanceBinaryTree tree = new BalanceBinaryTree(root);
for (int i = 1; i < arr.length; i++) tree.add(root, new Node(arr[i]));
tree.infixOrder(root);
System.out.println("\n" + "子树高度:" + tree.Height(root) + ",左子树高度:" + tree.leftHeight(root) + ",右子树高度:" + tree.rightHeight(root));
tree.leftRotate(root);
System.out.println("\n" + "子树高度:" + tree.Height(root) + ",左子树高度:" + tree.leftHeight(root) + ",右子树高度:" + tree.rightHeight(root));
tree.infixOrder(root);
}
3 4 5 6 7 8
子树高度:4,左子树高度:1,右子树高度:3
3 4 5 6 7 8
子树高度:3,左子树高度:2,右子树高度:2
3.右旋转
思路,以下图为例,以 “10” 节点为基准进行右旋转:
1.先创建一个新的 “10” 节点 new,新的 “10” 称为“new”,旧的“10”称为“old。
2.new 的右子树设为 old 的右子树。"10"->"12"
2.new的左子树设为 old 左子树的右子树 "10"->"9"
至此,新节点的左右子树都设置好了。
3. old的值换成其左子节点的值 ”10“=”8“
4.old的左子树设置为其左子树的左子树 "8"-> "7"
5.old的右子树设置为new "8"->"10"
至此,旧节点的左右子树都设置好了。
整个过程相当于把根节点的左节点当做新的根节点,把它向上拎了一下。
我们创建了一个新的 "10" 节点,它原来的位置被它的左子树 “8” 替换了,而 "8" 原来的位置相当于被废弃掉了,不会再访问到。
//5.右旋转
public void rightRotate(Node root) {
Node newNode = new Node(root.value);
newNode.left = root.left.right;
newNode.right = root.right;
root.value = root.left.value;
root.left = root.left.left;
root.right = newNode;
}
测试:
@Test
public void testRightRotate() {
int[] arr = {10, 12, 8, 9, 7, 6};
Node root = new Node(10);
BalanceBinaryTree tree = new BalanceBinaryTree(root);
for (int i = 1; i < arr.length; i++) tree.add(root, new Node(arr[i]));
tree.infixOrder(root);
System.out.println("\n" + "子树高度:" + tree.Height(root) + ",左子树高度:" + tree.leftHeight(root) + ",右子树高度:" + tree.rightHeight(root));
tree.rightRotate(root);
System.out.println("\n" + "子树高度:" + tree.Height(root) + ",左子树高度:" + tree.leftHeight(root) + ",右子树高度:" + tree.rightHeight(root));
tree.infixOrder(root);
}
6 7 8 9 10 12
子树高度:4,左子树高度:3,右子树高度:1
6 7 8 9 10 12
子树高度:3,左子树高度:2,右子树高度:2
4.双旋转
前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。比如数列
int[] arr = { 10, 11, 7, 6, 8, 9 }; 运行原来的代码可以看到,并没有转成 AVL树.
int[] arr = {2,1,6,5,7,3}; // 运行原来的代码可以看到,并没有转成 AVL树
解决方法:
1.如果要对 A 进行右旋转,先看下A的左子树的右子树高度是否大于A的左子树。如果满足就要先对A的左节点进行左旋转,然后再回过头对A进行右旋转。
1.如果要对 A 进行左旋转,先看下A的右子树的左子树高度是否大于A的右子树。如果满足就要先对A的右节点进行右旋转,然后再回过头对A进行左旋转。
这里我想到用递归的方法解决,这样思路比较清晰。双旋转包括了左右旋转的所有情况,具有普适性。我们从根节点递归地进行判断,然后旋转。
//6. 自动双旋转(从根往下递归旋转)
public void autoRotate(Node node) {
//希望左旋转
if (rightHeight(node) - leftHeight(node) >= 1) {
//右儿子出现问题了,它的左子树高度大于右子树高度
if (node.right != null && leftHeight(node.right) > rightHeight(node.right)) {
//递归到右儿子去
autoRotate(node.right);
}
//如果右儿子没出现问题,直接左旋转
leftRotate(node);
}
//希望右旋转
if (leftHeight(node) - rightHeight(node) >= 1) {
//左儿子出现问题了,它的右子树高度大于左子树高度
if (node.left != null && rightHeight(node.left) > leftHeight(node.left)) {
//递归到左儿子去
autoRotate(node.left);
}
//如果左儿子没出现问题,直接右旋转
rightRotate(node);
}
}
//7. 自动双向旋转的添加操作
public void autoRotateAdd(Node node) {
add(root, node);
autoRotate(root);
}
注:add方法是二叉排序树一节中的add方法,直接拿过来用。
测试:
为了验证普适性,我们各种参数都随机生成
@Test
public void testAutoRotateAdd() {
int j = 30;
while (j > 0) {
Random random = new Random();
int[] arr = new int[random.nextInt(50)+10];
for (int i = 0; i < arr.length; i++) arr[i] = random.nextInt(100);
Node root = new Node(arr[0]);
BalanceBinaryTree tree = new BalanceBinaryTree(root);
for (int i = 1; i < arr.length; i++) tree.autoRotateAdd(new Node(arr[i]));
tree.infixOrder(root);
System.out.println("\n" + "子树高度:" + tree.Height(root) + ",左子树高度:" + tree.leftHeight(root) + ",右子树高度:" + tree.rightHeight(root));
System.out.println();
j--;
}
}
1 4 5 5 7 10 11 11 11 12 13 16 18 20 24 29 32 34 34 37 38 38 44 48 48 50 50 51 51 52 52 53 57 65 66 69 70 71 73 74 78 79 80 82 87 93
子树高度:9,左子树高度:7,右子树高度:8
0 0 1 5 5 6 6 7 7 8 8 8 9 13 14 17 25 26 28 32 36 37 40 42 42 42 43 45 47 47 47 47 48 50 50 60 60 60 62 64 64 65 67 68 72 73 75 80 80 83 85 88 91 91 93 95 98
子树高度:10,左子树高度:9,右子树高度:9
0 1 1 2 7 8 11 12 15 20 21 25 26 27 30 31 35 35 36 38 38 41 42 44 46 52 52 53 58 58 62 63 63 66 69 71 73 74 75 76 76 77 80 80 80 81 81 85 86 87 87 89 93 95 96 96 97 97 99
子树高度:9,左子树高度:7,右子树高度:8
2 2 3 4 5 7 10 12 16 16 21 22 22 22 24 26 26 26 29 32 35 36 40 41 41 41 42 42 43 44 45 45 46 46 48 48 48 49 52 57 59 59 62 64 71 73 84 87 87 90 91 91 91 92 93 97 97
子树高度:10,左子树高度:8,右子树高度:9
1 2 3 5 7 10 12 12 13 14 15 15 16 20 24 24 24 27 28 30 38 39 44 46 47 47 48 56 57 58 60 63 67 67 68 70 77 78 79 81 82 82 83 83 84 87 93 93 95 95 95 95 97 99
子树高度:9,左子树高度:8,右子树高度:8
2 11 19 19 20 20 26 27 27 28 28 31 37 38 39 41 46 46 56 58 60 64 77 82 85 90 96 98
子树高度:7,左子树高度:6,右子树高度:6
8 11 16 17 19 27 29 29 33 33 41 44 49 50 52 53 55 58 59 63 66 68 68 72 75 79 80 81 83 84 85 87 94 98
子树高度:7,左子树高度:5,右子树高度:6
3 6 10 10 10 16 18 20 21 26 27 29 32 45 47 48 48 52 54 55 55 57 61 69 75 77 81 89 97 99
子树高度:7,左子树高度:5,右子树高度:6
0 1 4 5 7 9 9 12 12 12 13 14 15 17 18 21 24 24 24 27 33 35 36 38 43 45 46 47 50 57 58 58 58 62 62 66 67 71 74 77 77 82 82 83 83 85 86 88 88 88 89 91 95 96 97
子树高度:9,左子树高度:8,右子树高度:8
7 12 12 15 23 23 24 27 28 28 29 31 32 37 38 39 43 43 49 51 53 55 58 61 61 61 63 64 67 68 74 74 76 77 77 79 79 83 95 99
子树高度:9,左子树高度:8,右子树高度:8
5 6 21 24 26 30 31 42 44 66 90 91 94 95 99 99
子树高度:6,左子树高度:4,右子树高度:5
0 5 7 8 12 12 17 17 21 21 22 30 42 46 46 49 50 61 63 64 74 75 78 81 89 90 93 93 95
子树高度:7,左子树高度:5,右子树高度:6
16 22 24 24 27 33 46 51 54 58 60 74 80 81 86 87 88 94 94
子树高度:6,左子树高度:5,右子树高度:5
2 9 11 19 19 20 21 23 24 24 24 27 31 33 37 39 40 43 47 48 51 53 54 55 56 56 56 57 57 59 59 60 61 64 67 70 72 72 79 80 82 83 84 84 87 88 92 92 92 99 99
子树高度:9,左子树高度:7,右子树高度:8
0 7 8 17 18 21 25 26 27 31 32 36 43 44 44 48 50 52 52 53 54 56 57 61 61 72 74 76 77 79 91 91 91 91 93 96 98
子树高度:10,左子树高度:9,右子树高度:9
2 2 2 4 8 9 11 12 16 23 28 31 31 31 32 36 37 37 39 42 45 49 53 54 55 58 58 59 59 64 65 67 67 69 69 71 74 76 77 78 79 79 80 80 81 83 84 86 88 88 90 92 93 93 94 95 99 99
子树高度:9,左子树高度:8,右子树高度:8
4 6 6 7 9 12 15 17 18 21 22 23 24 27 27 28 28 31 32 33 33 33 34 36 36 38 38 44 45 47 49 54 54 55 55 61 62 64 64 68 75 79 81 82 86 87 87 90 90 91 91 93 97 97 99
子树高度:9,左子树高度:8,右子树高度:8
0 2 2 4 11 16 16 21 23 30 34 40 48 60 65 72 80 81 90 92 96 98 99
子树高度:7,左子树高度:5,右子树高度:6
3 8 10 11 14 14 14 19 20 20 23 26 27 29 30 31 31 40 41 46 46 48 50 51 51 53 53 58 63 68 70 73 73 73 76 78 79 79 81 83 84 84 86 90 91 91 92 93 96 99
子树高度:9,左子树高度:7,右子树高度:8
1 2 3 4 11 11 12 16 19 20 31 37 38 38 45 45 46 46 48 49 54 55 57 58 61 64 65 68 68 69 71 72 73 77 78 79 80 81 83 86 91 91 92 92 93 97 99
子树高度:9,左子树高度:8,右子树高度:8
0 2 2 10 10 11 16 17 19 20 26 28 28 36 41 46 48 50 54 70 72 74 75 81 88 88 92 93 94 95 95 96 99
子树高度:7,左子树高度:6,右子树高度:6
17 23 29 39 39 40 49 57 63 66 73 74 75 82 84 84 90
子树高度:6,左子树高度:5,右子树高度:5
3 4 24 33 36 37 48 48 86 99
子树高度:4,左子树高度:3,右子树高度:3
5 18 21 26 28 35 36 36 39 40 45 46 47 49 51 53 58 61 65 67 68 74 79 89 92 97
子树高度:7,左子树高度:5,右子树高度:6
11 17 18 24 27 35 38 40 46 67 67 87 95
子树高度:6,左子树高度:5,右子树高度:5
0 1 2 3 3 6 7 7 8 12 16 16 17 19 22 23 24 25 25 25 27 28 29 30 33 34 34 44 53 53 57 58 58 59 61 62 68 70 71 73 77 77 77 80 87 87 88 89 89 94 94 95 96 97 97 99 99 99
子树高度:9,左子树高度:7,右子树高度:8
8 8 11 19 27 36 40 41 44 57 62 69 73 76 77 81 93
子树高度:5,左子树高度:4,右子树高度:4
2 3 9 18 18 21 28 33 39 43 49 53 76 79 80 80 83 92 99
子树高度:6,左子树高度:4,右子树高度:5
5 10 19 22 26 39 44 47 52 66 73 74 83 97
子树高度:5,左子树高度:4,右子树高度:4
2 3 3 9 10 16 16 17 22 23 26 38 41 42 42 54 58 58 60 70 70 70 71 72 73 77 81 81 82
子树高度:9,左子树高度:8,右子树高度:8
四、总结
平衡二叉树AVL是对二叉排序树BST的优化,主要就是利用了左旋转、右旋转的策略实现左右深度不大于1的效果。结合左右旋转的方法可以实现双旋转,有很强的普适性。