数据结构——平衡二叉树(AVL树)

平衡二叉树

当且仅当任何节点的两棵子树的高度差不大于1的自平衡的二叉搜索树

原理

  • 平衡因子:某节点的左子树与右子树的高度差

  • 失衡节点:在插入/删除的结点向上查找,第一个平衡因子等于2的结点称为失衡节点

  • 通过旋转失衡节点,减少高度,将树重新调整为平衡二叉树

理解重点

平衡二叉树的增删和二叉搜索树的增删是一样的

不同在于平衡二叉树增删后可能需要旋转调整

不要边增删边旋转,这样会难以理解(增删后再旋转

平衡高度代码

balanceHeight()调整失衡节点,失衡节点的平衡因子等于2,故可分为4种情况

增加操作删除操作旋转操作
失衡节点的右节点的右子树增加节点删除左节点且失衡节点的右节点有(左)右子树对失衡节点左旋
失衡节点的右节点的左子树增加节点删除左节点且失衡节点的右节点仅有左子树对失衡节点的右节点右旋、对失衡节点左旋
失衡节点的左节点的左子树增加节点删除右节点且失衡节点的左节点有左(右)子树对失衡节点右旋
失衡节点的左节点的右子树增加节点删除右节点且失衡节点的左节点仅有右子树对失衡节点的左节点左旋、对失衡节点右旋
  • 失衡节点的平衡因子等于-2说明增加到右节点,再判断是增加到左子树还是右子树
  • 失衡节点的平衡因子等于2说明增加到左节点,再判断是增加左子树还是右子树
  • 删除节点相当于另一边增加节点(调整代码是一样的)
private void balanceHeight(TreeNode unBalancedNode) {
    if (unBalancedNode == null) {
        return;
    }
    if (getBalanceFactor(unBalancedNode) == -2) { 
        if (getBalanceFactor(unBalancedNode.rightChild) == 1) { 
            rightRotate(unBalancedNode.rightChild);
        }
        leftRotate(unBalancedNode);
    } else if (getBalanceFactor(unBalancedNode) == 2) {
        if (getBalanceFactor(unBalancedNode.leftChild) == -1) { 
            leftRotate(unBalancedNode.leftChild);
        }
        rightRotate(unBalancedNode);
    }
}

private void leftRotate(TreeNode unBalancedNode) {
    TreeNode unBalancedNodeParent = getParent(unBalancedNode);
    TreeNode temp = unBalancedNode.rightChild;
    if (unBalancedNodeParent == null) {  
        root = temp;
    } else {
        unBalancedNodeParent.leftChild = temp;     
    }
    unBalancedNode.rightChild = temp.leftChild; 
    temp.leftChild = unBalancedNode;  
}

private void rightRotate(TreeNode unBalancedNode) {
    TreeNode unBalancedNodeParent = getParent(unBalancedNode);
    TreeNode temp = unBalancedNode.leftChild;
    if (unBalancedNodeParent == null) {
        root = temp;
    } else {
        unBalancedNodeParent.rightChild = temp;
    }
    unBalancedNode.leftChild = temp.rightChild;
    temp.rightChild = unBalancedNode;
}

leftRotate()实现指定节点的左旋,具体为(右旋对称同理):

  • 获取其父节点和其右节点
  • 若其父节点为空,则其右节点称为新父节点,否则让其父节点指向其右节点(即断开指定节点和其父节点)
  • 指定节点的右节点指向其右节点的左节点(即断开指定节点和其右节点,拿到其右节点的左节点)
  • 其右节点的左节点指向该节点(即连回指定节点,右节点变成指定节点的父节点)

左旋示意图
请添加图片描述
右旋示意图
请添加图片描述

代码实现

  • innerMidOrderTraversal():中序遍历验证旋转结果
  • innerAdd():从二叉搜索树复制过来的代码,返回增加的节点
  • innerSearch()、MinNode()、getSuc()、InnerRemove():从二叉搜索树复制过来的代码,为了找到待删除节点和链接后序节点,返回删除的节点
  • getTreeHeight()、getBalanceFactor()、getUnbalancedNode():获取节点高度、平衡因子、失衡节点
  • add()、remove():先进行二叉搜索树的增删,返回增删节点,找到对应的失衡节点,调用balanceHeight()
class BalancedBinaryTree {

    private class TreeNode {

        int value;
        TreeNode leftChild;
        TreeNode rightChild;

        TreeNode(int value) {
            this.value = value;
        }
    }

    private TreeNode root;

    private void innerMidOrderTraversal(TreeNode root) {
        if (root == null) {
            return;
        }
        innerMidOrderTraversal(root.leftChild);
        System.out.print(root.value + " ");
        innerMidOrderTraversal(root.rightChild);
    }

    private void leftRotate(TreeNode unBalancedNode) {
        TreeNode unBalancedNodeParent = getParent(unBalancedNode);
        TreeNode temp = unBalancedNode.rightChild;
        if (unBalancedNodeParent == null) {                 //若失衡节点为根节点,则失衡节点的右节点为新根节点
            root = temp;
        } else {
            unBalancedNodeParent.leftChild = temp;          //失衡节点的父节点指向其右节点
        }
        unBalancedNode.rightChild = temp.leftChild;     //失衡节点的右节点指向其右节点的左节点
        temp.leftChild = unBalancedNode;                //连回失衡节点
    }

    private void rightRotate(TreeNode unBalancedNode) {
        TreeNode unBalancedNodeParent = getParent(unBalancedNode);
        TreeNode temp = unBalancedNode.leftChild;
        if (unBalancedNodeParent == null) {
            root = temp;
        } else {
            unBalancedNodeParent.rightChild = temp;
        }
        unBalancedNode.leftChild = temp.rightChild;
        temp.rightChild = unBalancedNode;
    }

    private int getTreeHeight(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int leftDepth = getTreeHeight(node.leftChild) + 1;
        int rightDepth = getTreeHeight(node.rightChild) + 1;
        return Math.max(leftDepth, rightDepth);
    }

    private int getBalanceFactor(TreeNode node) {
        return getTreeHeight(node.leftChild) - getTreeHeight(node.rightChild);
    }

    private TreeNode getUnbalancedNode(TreeNode node) {
        TreeNode parent = getParent(node);
        while (parent != null) {
            if (Math.abs(getBalanceFactor(parent)) > 1) {	// ==2
                return parent;
            }
            parent = getParent(parent);
        }
        return null;
    }

    private TreeNode getParent(TreeNode node) {
        if (root == node) {
            return null;
        }
        if (node == null) {
            return null;
        }
        TreeNode current = root;
        TreeNode parent = null;
        while (current != null) {
            if (node.value < current.value) {
                parent = current;
                current = current.leftChild;
            } else if (node.value > current.value) {
                parent = current;
                current = current.rightChild;
            } else {
                return parent;
            }
        }
        return parent;
    }

    private void balanceHeight(TreeNode unBalancedNode) {
        if (unBalancedNode == null) {
            return;
        }
        System.out.print("旋转前 ——> 中序遍历:");
        innerMidOrderTraversal(root);
        System.out.println();

        if (getBalanceFactor(unBalancedNode) == -2) {   //删除左节点 == 右节点添加
            if (getBalanceFactor(unBalancedNode.rightChild) == 1) {     //右节点的左子树添加
                System.out.println("即将右旋");
                rightRotate(unBalancedNode.rightChild);
            }
            System.out.println("即将左旋");
            leftRotate(unBalancedNode);
        } else if (getBalanceFactor(unBalancedNode) == 2) {     //删除右节点 == 左节点添加
            if (getBalanceFactor(unBalancedNode.leftChild) == -1) {     //左节点的右子树添加
                System.out.println("即将左旋");
                leftRotate(unBalancedNode.leftChild);
            }
            System.out.println("即将右旋");
            rightRotate(unBalancedNode);
        }

        System.out.print("旋转后 ——> 中序遍历:");
        innerMidOrderTraversal(root);
        System.out.println();
    }

    public void add(int value) {
        TreeNode addNode = innerAdd(value);
        System.out.print("添加节点为: " + addNode.value);

        TreeNode unBalancedNode = getUnbalancedNode(addNode);
        System.out.println(" ——> 失衡节点为: " + (unBalancedNode == null ? "null" : unBalancedNode.value));

        balanceHeight(unBalancedNode);
    }

    private TreeNode innerAdd(int value) {
        if (root == null) {
            root = new TreeNode(value);
            return root;
        } else {
            TreeNode parent;
            TreeNode current = root;
            while (true) {
                parent = current;
                if (value < current.value) {
                    current = current.leftChild;
                    if (current == null) {
                        parent.leftChild = new TreeNode(value);
                        return parent.leftChild;
                    }
                } else {
                    current = current.rightChild;
                    if (current == null) {
                        parent.rightChild = new TreeNode(value);
                        return parent.rightChild;
                    }
                }
            }
        }
    }

    private TreeNode innerSearch(TreeNode root, int value) {
        if (root == null) {
            return null;
        }
        if (value < root.value) {
            return innerSearch(root.leftChild, value);
        } else if (value > root.value) {
            return innerSearch(root.rightChild, value);
        } else {
            return root;
        }
    }

    private TreeNode MinNode(TreeNode node) {
        if (node == null) {
            return null;
        }
        TreeNode current = node;
        while (current.leftChild != null) {
            current = current.leftChild;
        }
        return current;
    }

    private TreeNode getSuc(TreeNode node) {
        if (node.rightChild != null) {
            return MinNode(node.rightChild);
        } else {
            TreeNode parent = getParent(node);
            if (parent == null) {
                return null;
            }
            if (node == parent.leftChild) {
                return parent;
            } else {    //node == parent.rightChild
                while (parent != null) {
                    if (parent.value > node.value) {
                        break;
                    }
                    parent = getParent(node);
                }
                return parent;
            }
        }
    }

    public void remove(int value) {
        TreeNode delNode = InnerRemove(value);
        System.out.print("删除节点为: " + delNode.value);

        TreeNode unBalancedNode = getUnbalancedNode(delNode);
        System.out.println(" ——> 失衡节点为: " + (unBalancedNode == null ? "null" : unBalancedNode.value));

        balanceHeight(unBalancedNode);
    }

    private TreeNode InnerRemove(int value) {
        TreeNode delNode = innerSearch(root, value);
        TreeNode parent = getParent(delNode);
        if (delNode.leftChild == null && delNode.rightChild == null) {  //删除的节点无左右节点,则断开父节点和它的连接(若是根节点则置空)
            if (parent == null) {   //delNode = root
                root = null;
            } else {
                if (delNode == parent.leftChild) {
                    parent.leftChild = null;
                } else {
                    parent.rightChild = null;
                }
            }
        } else if (delNode.leftChild == null && delNode.rightChild != null) { //删除的节点有右节点,则让其右节点连上父节点(若是根节点,则右节点成为新根节点)
            if (parent == null) {   //delNode = root
                root = delNode.rightChild;
            } else {
                if (delNode == parent.leftChild) {
                    parent.leftChild = delNode.rightChild;
                } else {
                    parent.rightChild = delNode.rightChild;
                }
            }
        } else if (delNode.leftChild != null && delNode.rightChild == null) { //删除的节点有左节点,则让其左节点连上父节点(若是根节点,则左节点成为新根节点)
            if (parent == null) {   //delNode = root
                root = delNode.leftChild;
            } else {
                if (delNode == parent.leftChild) {
                    parent.leftChild = delNode.leftChild;
                } else {
                    parent.rightChild = delNode.leftChild;
                }
            }
        } else { //删除的节点左右节点都有
            if (parent == null) {   //delNode = root,则左右节点任意一个都可成为新根节点
                root = delNode.leftChild;
                //root = delNode.rightChild;
            } else {
                TreeNode successor = getSuc(delNode);
                TreeNode successorParent = getParent(successor);
                if (successorParent == delNode) {   //若后继节点是删除节点的右节点
                    parent.rightChild = successor;
                    successor.leftChild = delNode.leftChild;
                    delNode.leftChild = null;
                } else {                  //若后继节点是删除节点的右子树的左节点
                    successorParent.leftChild = successor.rightChild;   //断开successor
                    successor.rightChild = null;

                    parent.leftChild = successor;               //del父节点指向successor,即断开del
                    successor.rightChild = delNode.rightChild;  //successor连接delNode右子树
                    delNode.rightChild = null;                  //断开delNode右子树

                    successor.leftChild = delNode.leftChild;    //successor连接delNode左子树
                    delNode.leftChild = null;                   //断开delNode左子树
                }
            }
        }
        return delNode;
    }
}

代码测试

这里不讲解平衡二叉树的增删步骤代码,如果不知道请看二叉搜索树

增加测试

失衡节点的右节点的右子树增加节点

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.add(9);
balancedBinaryTree.add(10); 

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 10 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 5 7 8 9 10 
即将左旋
旋转后 ——> 中序遍历:3 5 7 8 9 10 

失衡节点的右节点的左子树增加节点

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.add(9);
balancedBinaryTree.add(6);

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 5 6 7 8 9 
即将右旋
即将左旋
旋转后 ——> 中序遍历:3 5 6 7 8 9 

失衡节点的左节点的左子树增加节点

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(8);
balancedBinaryTree.add(5);
balancedBinaryTree.add(9);
balancedBinaryTree.add(3);
balancedBinaryTree.add(7);
balancedBinaryTree.add(2);

打印如下

添加节点为: 8 ——> 失衡节点为: null
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 2 ——> 失衡节点为: 8
旋转前 ——> 中序遍历:2 3 5 7 8 9 
即将右旋
旋转后 ——> 中序遍历:2 3 5 7 8 9 

失衡节点的左节点的右子树增加节点

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(8);
balancedBinaryTree.add(5);
balancedBinaryTree.add(9);
balancedBinaryTree.add(3);
balancedBinaryTree.add(7);
balancedBinaryTree.add(6);

打印如下

添加节点为: 8 ——> 失衡节点为: null
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: 8
旋转前 ——> 中序遍历:3 5 6 7 8 9 
即将左旋
即将右旋
旋转后 ——> 中序遍历:3 5 6 7 8 9 

删除测试

删除左节点且失衡节点的右节点有(左)右子树

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(9);
//balancedBinaryTree.add(7);
balancedBinaryTree.remove(3);

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
删除节点为: 3 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:5 8 9 
即将左旋
旋转后 ——> 中序遍历:5 8 9 

删除左节点且失衡节点的右节点仅有左子树

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.remove(3);

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
删除节点为: 3 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:5 7 8 
即将右旋
即将左旋
旋转后 ——> 中序遍历:5 7 8 

删除右节点且失衡节点的左节点有左(右)子树

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(6);
balancedBinaryTree.add(2);
//balancedBinaryTree.add(4);
balancedBinaryTree.remove(6);

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: null
添加节点为: 2 ——> 失衡节点为: null
删除节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:2 3 5 
即将右旋
旋转后 ——> 中序遍历:2 3 5 

删除右节点且失衡节点的左节点仅有右子树

BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(6);
balancedBinaryTree.add(4);
balancedBinaryTree.remove(6);

打印如下

添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: null
添加节点为: 4 ——> 失衡节点为: null
删除节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 4 5 
即将左旋
即将右旋
旋转后 ——> 中序遍历:3 4 5 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值