平衡二叉树的原理及实现

今天分析平衡二叉树的原理及实现:

详解平衡二叉树之前先大致普及一下二叉树的基础。

一、二叉树

1、定义

1)结点的度:结点的子树个数

2)树的度:树中所有结点中最大的度

3)结点的层次:规定根结点在1层,子结点的层数是它父结点的层数加1

4)树的高度:树中所有结点中最大的层次是这棵树的高度

度为2的树(树中所有结点中最大的度) ,子树有左右顺序之分    

2、二叉排序树

二叉排序树(BST,Binary Sort Tree) 也称二叉查找树(Binary Search Tree),或二叉搜索树。

定义:一颗二叉树,满足以下属性

左子树的所有的值小于根节点的值;右子树的所有的值大于根节点的值 。左、右子树满足以上两点。

错误示例:

正确示例:

 

3、二叉排序树的查找操作:Find

查找的值X从根节点开始

1)如果X小于根节点值,则在左子树中继续查找;

2)如果X大于根节点值,则在右子树中继续查找;

3)如果X值等于根节点值,则返回该节点;

4)如果都查不到,则返回空Null;

5)查找的效率决定于树的高度

6)最大元素一定是在树的最右分支的节点上

7)最小元素一定是在树的最左分支的节点上

 

4、二叉排序树的插入操作:Insert

插入的值X从根节点开始查找

1)X值小于该节点值,在左子树中继续;

2)X值大于该节点值,在右子树中继续;

3)如果节点是叶节点,X值小于该节点值则插入左子节点,否则插入右节点;

 

5、二叉排序树的删除操作:Delete

插入的值X从根节点开始查找

1)如果节点的值等于X,则删除;

2)X值小于该节点值,在左子树中继续;

3)X值大于该节点值,在右子树中继续;

二、平衡二叉树

1、平衡二叉树定义

平衡二叉树(AVL树,Balance Binary Search Tree )

它是一 棵二叉排序树,它的左右两个子树的高度差(平衡因子)的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树。

目的:使得树的高度最低,因为树查找的效率决定于树的高度。

示例:错误与正确的示例。

2、平衡二叉树的调整:

如果A是一颗平衡二叉树,如果新插入一个元素,会有两个结果:

1)平衡没有被打破,不用调整。

2)平衡被打破,需要调整。

3)调整原则:根据插入节点与失衡结点的位置关系来划分。

(左右旋时新的根节点多出来一个节点重新调整指向)左旋时——自己(多出来的)变为右孩子的左孩子;右旋——自己(多出来的)变为左孩子的右孩子。

3、RR旋转

插入节点在失衡结点的左子树的左边 只需要经过一次右旋即可达到平衡,右旋——自己(多出来的0025节点)变为左孩子的右孩子。

右旋动态图:

这里写图片描述 

4、LL旋转

插入节点在失衡结点的右子树的右边 只需要经过一次左旋即可达到平衡,右旋——自己(多出来的0025节点)变为左孩子的右孩子。

左旋动态图:

 这里写图片描述

5、LR旋转

需要进行两次旋转才能达到平衡

1)插入节点在失衡结点的左子树的右边

2)失衡结点的左子树先做LL旋转

3)失衡结点再做RR旋转即可达到平衡

在这里插入图片描述

6、RL旋转

需要进行两次旋转才能达到平衡

1)插入节点在失衡结点的右子树的左边

2)失衡结点的右子树先做RR旋转

3)失衡结点再做LL旋转即可达到平衡

7、java代码实现:

public class NandaoAVLTree {
    //节点
    public static class Node {
        int data; //数据

        Node leftChild; //左子节点
        Node rightChild;//右子节点
        int height; // 记录该节点所在的高度

        public Node(int data) {
            this.data = data;
        }
    }
    //获取节点的高度
    public static int getHeight(Node p){
        return p == null ? -1 : p.height; // 空树的高度为-1
    }
    public static void main(String[] args) {
        Node root = null;
        root = insert(root,30);
        root = insert(root,20);
        root = insert(root,40);
        root = insert(root,10);
        root = insert(root,25);
        //插入节点在失衡结点的左子树的左边
        root = insert(root,5);
        //打印树,按照先打印左子树,再打印右子树的方式
        printTree(root);

    }

    public static void printTree(Node root) {
        System.out.println(root.data);
        if(root.leftChild !=null){
            System.out.print("left:");
            printTree(root.leftChild);
        }
        if(root.rightChild !=null){
            System.out.print("right:");
            printTree(root.rightChild);
        }
    }
    // AVL树的插入方法
    public static Node insert(Node root, int data) {
        if (root == null) {
            root = new Node(data);
            return root;
        }
        if (data <= root.data) { // 插入到其左子树上
            root.leftChild = insert(root.leftChild, data);
            //平衡调整
            if (getHeight(root.leftChild) - getHeight(root.rightChild) > 1) {
                if (data <= root.leftChild.data) { // 插入节点在失衡结点的左子树的左边
                    System.out.println("RR旋转");
                    root = RRRotate(root); // RR旋转调整
                }else{ // 插入节点在失衡结点的左子树的右边
                    System.out.println("LR旋转");
                    root = LRRotate(root);
                }
            }
        }else{ // 插入到其右子树上
            root.rightChild = insert(root.rightChild, data);
            //平衡调整
            if(getHeight(root.rightChild) - getHeight(root.leftChild) > 1){
                if(data <= root.rightChild.data){//插入节点在失衡结点的右子树的左边
                    System.out.println("RL旋转");
                    root = RLRotate(root);
                }else{
                    System.out.println("LL旋转");//插入节点在失衡结点的右子树的右边
                    root = LLRotate(root);
                }
            }
        }
        //重新调整root节点的高度值
        root.height = Math.max(getHeight(root.leftChild), getHeight(root.rightChild)) + 1;
        return root;
    }
    // LR旋转
    public static Node LRRotate(Node p){
        p.leftChild = LLRotate(p.leftChild); // 先将失衡点p的左子树进行LL旋转
        return RRRotate(p); // 再将失衡点p进行LL平衡旋转并返回新节点代替原失衡点p

    }
    // RL平衡旋转
    public static Node RLRotate(Node p){
        p.rightChild = RRRotate(p.rightChild); // 先将失衡点p的右子树进行RR平衡旋转
        return LLRotate(p); // 再将失衡点p进行LL平衡旋转并返回新节点代替原失衡点p
    }

    /*
     * RR旋转
     * 左旋示意图(对结点20进行左旋)
     *      30                       20
     *     /  \                     /  \
     *    20  40                  10   30
     *   /  \      --RR旋转-       /   /  \
     *  10   25                  5   25   40
     *  /
     * 5
     *
     */
    public static Node RRRotate(Node p){ // 30为失衡点
        Node lsubtree = p.leftChild;   //失衡点的左子树的根结点20作为新的结点
        p.leftChild = lsubtree.rightChild; //将新节点的右子树25成为失衡点30的左子树
        lsubtree.rightChild = p; // 将失衡点30作为新结点的右子树
        // 重新设置失衡点30和新节点20的高度
        p.height = Math.max(getHeight(p.leftChild), getHeight(p.rightChild)) + 1;
        lsubtree.height = Math.max(getHeight(lsubtree.leftChild), p.height) + 1;
        return lsubtree; // 新的根节点取代原失衡点的位置
    }
    /*
     * LL旋转
     * 右旋示意图(对结点30进行左旋)
     *      20                          30
     *     /  \                        /  \
     *    10  30                     20   40
     *       /  \      --LL旋转-     /  \   \
     *      25  40                 10  25  50
     *           \
     *           50
     *
     */
    // LL旋转
    public static Node LLRotate(Node p){ // 20为失衡点
        Node rsubtree = p.rightChild;  //失衡点的右子树的根结点30作为新的结点
        p.rightChild = rsubtree.leftChild; //将新节点的左子树25成为失衡点20的右子树
        rsubtree.leftChild = p; // 将失衡点20作为新结点的左子树
        // 重新设置失衡点20和新节点30的高度
        p.height = Math.max(getHeight(p.leftChild), getHeight(p.rightChild)) + 1;
        rsubtree.height = Math.max(getHeight(rsubtree.leftChild), getHeight(rsubtree.rightChild)) + 1;
        return rsubtree; // 新的根节点取代原失衡点的位置
    }
}

8、执行结果:

到此,平衡二叉树分享完毕,下篇我们分享红黑树的原理和实现,敬请期待!

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
平衡二叉树是一种特殊的二叉搜索树,它的左右子树的高度差不超过1,以此来保证树的高度不会太高,从而保证树的查询、插入、删除等操作的时间复杂度都能在O(logn)的范围内。底层实现主要有以下几点: 1. 节点结构体:平衡二叉树的节点结构体通常包括关键字key、左右子树的指针、平衡因子等成员变量。平衡因子是左右子树高度差的绝对值,是用来判断是否需要进行旋转操作的。 2. 插入操作:插入节点时,首先按照二叉搜索树的规则找到合适的位置,插入新节点。然后从新节点到根节点依次更新每个节点的平衡因子,判断是否需要进行旋转操作。如果某个节点的平衡因子超过1,则需要进行旋转操作来保证树的平衡性。 3. 旋转操作:旋转操作是平衡二叉树的核心操作,包括左旋、右旋、左右旋、右左旋等四种。左旋和右旋是最基本的旋转操作,左右旋和右左旋是由左旋和右旋组合而成的复合操作。旋转操作可以通过改变节点和子树的指针来实现,具体操作可以参考相关算法书籍和资料。 4. 删除操作:删除节点时,需要先按照二叉搜索树的规则找到待删除节点,然后考虑三种情况:如果待删除节点是叶子节点,则直接删除;如果待删除节点只有一棵子树,则将该子树的根节点替换待删除节点;如果待删除节点有两棵子树,则找到其前驱或后继节点替换待删除节点,并将其前驱或后继节点删除。删除节点后,需要从其父节点到根节点依次更新每个节点的平衡因子,判断是否需要进行旋转操作。 总之,平衡二叉树的底层实现需要掌握节点结构体、插入操作、旋转操作和删除操作等关键要点,只有深入理解这些要点,才能真正理解平衡二叉树的本质和优点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值