平衡二叉树AVL的机制

平衡二叉树(AVL树)

平衡二叉树也叫平衡二叉搜索树,又被称之为AVL树,可以保证查询效率较高

具有以下特点:它是一棵空树或者它的左右两个子树的高度差绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.平衡二叉树的常用实现方法有红黑树,AVL,替罪羊树,Treap,伸展树等

AVL的节点删除

AVL的节点删除和二叉搜索树相同.

旋转操作

在添加节点时,为了使得整棵树处于平衡状态,需要进行AVL的旋转.

注意,旋转操作一定不能改变当前节点的指向(即修改前后,temp(当前节点)始终处于顶部,不能将创建的新节点置于顶部,否则当temp节点为根节点时,则需要额外操作来将新节点修改为整棵树的根节点)

左旋转

当右子树高度 - 左子树高度 > 1时:

  1. 以当前节点的值建立新节点
  2. 新节点的左边,牵上当前节点的左边
  3. 新节点的右边,牵上当前节点右节点的左节点
  4. 修改当前节点的id值,为其右边节点的值(连接尚未断开)
  5. 当前节点左边牵上新节点
  6. 当前节点的连接跳过复制节点id的那个节点(删除)
  • 左旋转代码:
//左旋转(右子树高度 - 左子树高度 > 1)
private void leftRotate(Node temp)
{
    //以当前节点的值建立新节点
    Node newNode = new Node(temp.id);
    //新节点的左边,牵上当前节点的左边
    newNode.left = temp.left;
    //新节点的右边,牵上当前节点右边的左边
    newNode.right = temp.right.left;
    //修改当前节点的id值,为其右边节点的值(连接尚未断开)
    temp.id = temp.right.id;
    //当前节点左边牵上新节点
    temp.left = newNode;
    //跳过复制节点id的那个节点(删除)
    temp.right = temp.right.right;
}

右旋转

当左子树高度 - 右子树高度 > 1时:

  1. 以当前节点的值建立新节点
  2. 新节点的右边,牵上当前节点的右边
  3. 新节点的左边,牵上当前节点左节点的右节点
  4. 修改当前节点的id值,为其左节点的值(连接尚未断开)
  5. 当前节点右边牵上新节点
  6. 当前节点的连接跳过复制节点id的那个节点(删除)
  • 图例如下:

在这里插入图片描述

  • 右旋转代码:
//右旋转(左子树高度 - 右子树高度 > 1)
private void rightRotate(Node temp)
{
    Node newNode = new Node(temp.id);
    newNode.right = temp.right;
    newNode.left = temp.left.right;
    temp.id = temp.left.id;
    temp.right = newNode;
    temp.left = temp.left.left;
}

双旋转

  • 左旋->右旋:
  1. 先满足右旋转的条件
  2. 当前节点左节点的右子树高度大于左子树的高度(内侧树高度大于外侧树高度)
  3. 先对当前节点的左节点进行左旋转
  4. 再对当前节点进行右旋转

在这里插入图片描述

  • 右旋->左旋:
  1. 先满足左旋转的条件
  2. 当前节点右节点的左子树高度大于右子树的高度(内侧树高度大于外侧树高度)
  3. 先对当前节点的右节点进行右旋转
  4. 再对当前节点进行左旋转

旋转部分代码(所有旋转的逻辑),旋转操作增添至二叉排序树增加节点方法的尾部:

//满足左旋转条件
if(temp.rightHeight() - temp.leftHeight() > 1)
{
    //内侧子树的高度大于外侧子树的高度,不能单纯地进行左旋,要进行双旋转
    if(temp.right.leftHeight() > temp.right.rightHeight())
    {
        rightRotate(temp.right);
    }
    //整体左旋
    leftRotate(temp);
}
//满足右旋转条件
else if(temp.leftHeight() - temp.rightHeight() > 1)
{
    //内侧子树的高度大于外侧子树的高度,不能单纯地进行右旋,要进行双旋转
    if(temp.left.rightHeight() > temp.left.leftHeight())
    {
        rightRotate(temp.left);
    }
    //整体右旋
    rightRotate(temp);
}

AVL树完整类代码:

//平衡二叉树
public class AVLTree
{
    //根节点
    Node root;

    //空参构造方法
    public AVLTree() { }
    //有参构造方法
    public AVLTree(int id)
    {
        root = new Node(id);
    }


    /**
     * 增加节点的函数,包含AVL的旋转操作
     * @param temp temp为当前递归至的节点
     * @param id   id为需要增加的新节点的id
     */
    public void addNode(Node temp,int id)
    {
        //新节点从属于在当前节点的左子树
        if(id < temp.id)
        {
            //左边为空,直接挂在左边
            if(temp.left == null)
            {
                temp.left = new Node(id);
                return;
            }
            //左边不为空,则向左边递归
            else
            {
                addNode(temp.left,id);
            }
        }
        //新节点从属于当前节点的右子树
        else
        {
            //右边为空,直接挂在左边
            if(temp.right == null)
            {
                temp.right = new Node(id);
                return;
            }
            //右边不为空,则向右边递归
            else
            {
                addNode(temp.right,id);
            }
        }
        //满足左旋转条件
        if(temp.rightHeight() - temp.leftHeight() > 1)
        {
            //内侧子树的高度大于外侧子树的高度,不能单纯地进行左旋,要进行双旋转
            if(temp.right.leftHeight() > temp.right.rightHeight())
            {
                rightRotate(temp.right);
            }
            leftRotate(temp);
        }
        //满足右旋转条件
        else if(temp.leftHeight() - temp.rightHeight() > 1)
        {
            //内侧子树的高度大于外侧子树的高度,不能单纯地进行右旋,要进行双旋转
            if(temp.left.rightHeight() > temp.left.leftHeight())
            {
                rightRotate(temp.left);
            }
            rightRotate(temp);
        }
    }
    //方法重写
    public void addNode(int id)
    {
        //根节点为空,则直接修改根节点
        if(root == null)
        {
            root = new Node(id);
            return;
        }
        //根节点不为空,执行之前写好的方法
        addNode(root,id);
    }

    //中序遍历
    public void traversal()
    {
        if(root == null)
        {
            System.out.println("二叉树为空!");
            return;
        }
        //调用节点类中地中序遍历方法
        root.traversal();
        System.out.println();   //换行符
    }

    //删除节点
    /**
     * 删除节点方法重写
     * @param target 当前目标节点
     * @param id     要删除的节点id值
     * @return       返回的节点为target连接的下一个节点
     */
    public Node delNode(Node target,int id)
    {
        if(target.id == id)
        {
            //1. 要删除的是叶子节点
            if(target.left == null && target.right == null)
            {
                return null;
            }
            //2. 要删除的节点,其含有左子树,但不含有右子树
            else if(target.left != null && target.right == null)
            {
                return target.left;
            }
            //3. 要删除的节点,其含有左子树,但不含有右子树
            else if(target.left == null && target.right != null)
            {
                return target.right;
            }
            //4. 要删除的节点,同时具有左子树和右子树
            else
            {
                Node cur = target.right;
                while(cur.left != null)
                {
                    cur = cur.left;
                }
                cur.left = target.left;
                return target.right;
            }
        }
        //递归删除左边节点
        else if(id < target.id)
        {
            target.left = delNode(target.left,id);
        }
        //递归删除右边节点
        else if(id > target.id)
        {
            target.right = delNode(target.right,id);
        }
        return target;
    }
    //方法重写
    public void delNode(int id)
    {
        if(root == null)
        {
            System.out.println("二叉树为空!无法删除!");
            return;
        }
        //如果root即为要删除的节点
        if(root.id == id)
        {
            //根节点为叶子节点,直接置空
            if(root.left == null && root.right == null)
            {
                root = null;
            }
            //根节点左边不为空,右边为空
            else if(root.left != null && root.right == null)
            {
                root = root.left;
            }
            //根节点左边为空,右边不为空
            else if(root.left == null && root.right != null)
            {
                root = root.right;
            }
            //根节点左右节点皆不为空
            else
            {
                Node cur = root.right;
                while(cur.left != null)
                {
                    cur = cur.left;
                }
                cur.left = root.left;
                root = root.right;
            }
            return;
        }
        //根节点不是要删除的节点,则调用重写中的方法
        Node nodeDel = delNode(root,id);
    }

    //左旋转(右子树高度 - 左子树高度 > 1)
    private void leftRotate(Node temp)
    {
        //以当前节点的值建立新节点
        Node newNode = new Node(temp.id);
        //新节点的左边,牵上当前节点的左边
        newNode.left = temp.left;
        //新节点的右边,牵上当前节点右边的左边
        newNode.right = temp.right.left;
        //修改当前节点的id值,为其右边节点的值(连接尚未断开)
        temp.id = temp.right.id;
        //当前节点左边牵上新节点
        temp.left = newNode;
        //跳过复制节点id的那个节点(删除)
        temp.right = temp.right.right;
    }
    //右旋转(左子树高度 - 右子树高度 > 1)
    private void rightRotate(Node temp)
    {
        Node newNode = new Node(temp.id);
        newNode.right = temp.right;
        newNode.left = temp.left.right;
        temp.id = temp.left.id;
        temp.right = newNode;
        temp.left = temp.left.left;
    }
}

//节点类
class Node
{
    int id;
    Node left;
    Node right;

    //构造函数
    public Node(int id)
    {
        this.id = id;
    }

    //转化为字符串的方法
    @Override
    public String toString()
    {
        return "id=" + id;
    }

    //遍历
    public void traversal()
    {
        if(this.left != null)
        {
            this.left.traversal();
        }
        System.out.print(this + " ");
        if(this.right != null)
        {
            this.right.traversal();
        }
    }

    //以当前节点为根部的树的高度
    public int height()
    {
        if(this == null)
        {
            return 0;
        }
        return Math.max(this.left == null ? 0 : this.left.height(),this.right == null ? 0 : this.right.height()) + 1;
    }
    //左子树的高度(递归方法求树高)
    public int leftHeight()
    {
        return this.left == null ? 0 : this.left.height();
    }
    //右子树的高度(递归方法求树高)
    public int rightHeight()
    {
        return this.right == null ? 0 : this.right.height();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

#冷咖啡离开了杯垫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值