[ 数据结构 ] 平衡二叉树(AVL)--------左旋、右旋、双旋

文章讲述了如何构建二叉排序树,并讨论了当树失去平衡时查询效率下降的问题。为了解决这个问题,文章介绍了平衡二叉树的概念,特别是AVL树,以及如何通过左旋、右旋和双旋操作来保持树的平衡,从而确保高效的查询性能。示例代码展示了如何在插入节点后进行旋转操作以维护AVL树的平衡。
摘要由CSDN通过智能技术生成

0 引出

数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在 回顾:二叉搜索树

image-20230108221827379.png

  1. 左子树全部为空,从形式上看,更像一个单链表.
  2. 插入速度没有影响
  3. 查询速度明显降低(因为需要依次比较), 不能发挥 BST的优势,因为每次还需要比较左子树,其查询速度比单链表还慢
  4. 解决方案-平衡二叉树(AVL)

1 平衡二叉树

  1. 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为 AVL 树, 可以保证查询效率较高。
  2. 具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等
  3. 如何保证在创建二叉排序树的过程中,一直都满足平衡二叉树呢?
  4. 在添加节点add方法的末尾判断(也可以说在非叶子节点处):如果左右子树高度差>1,那么考虑左旋/右旋/双旋
//添加节点
    public void add(BNode bnode) {
        if (bnode == null) {
            return;
        }
        if (bnode.value < this.value) {
            if (this.left == null) {
                this.left = bnode;
            } else {
                this.left.add(bnode);
            }
        } else {
            if (this.right == null) {
                this.right = bnode;
            } else {
                this.right.add(bnode);
            }
        }

        //考虑左旋
//        if (rightTreeHeight() - leftTreeHeight() > 1) {
//            leftRotate();
//        }
        //考虑右旋
//        if (leftTreeHeight()-rightTreeHeight() > 1) {
//            rightRotate();
//        }
        //考虑双旋(单独的左旋/右旋可能不能解决问题)
        if (rightTreeHeight() - leftTreeHeight() > 1) {
            if (right != null && right.leftTreeHeight() > right.rightTreeHeight()) {
                right.rightRotate();
            }
            leftRotate();
            return;//省的走下面代码,影响效率(因为涉及递归调用)
        }
        if (leftTreeHeight()-rightTreeHeight() > 1) {
            if (left != null && left.leftTreeHeight() < left.rightTreeHeight()) {
                left.leftRotate();
            }
            rightRotate();
        }
    }
    
    //返回当前节点的高度
    public int getHeight() {
        return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight())+1;
    }
    //左子树高度
    public int leftTreeHeight() {
        if (left == null) {
            return 0;
        } else {
            return left.getHeight();
        }
    }
    //右子树高度
    public int rightTreeHeight() {
        if (right == null) {
            return 0;
        } else {
            return right.getHeight();
        }
    }

2 左旋

image-20230108223734856.png

  1. 主要操作就是改变左旋节点4和其右子节点6的指针指向
  2. 首先拷贝4,新的4左指向3,右指向5
  3. 原来的4改为6,左指向新的4,右指向7
  4. 原来的6由于没有被指向,GC回收
  5. 左旋完成
//左旋
    public void leftRotate() {
        BNode newNode = new BNode(value);
        newNode.left = this.left;
        newNode.right = this.right.left;
        this.setValue(right.value);
        this.setLeft(newNode);
        this.setRight(this.right.right);
    }

3 右旋

image-20230109115239759.png

  1. 属于左旋的镜像操作,改变右旋节点10和其左子节点8的指针指向
  2. 首先拷贝10,新的10左指向9,右指向12
  3. 原来的10改为8,左指向7,右指向新的10
  4. 原来的8由于没有被指向,GC回收
  5. 右旋完成
//右旋
    public void rightRotate() {
        BNode newNode = new BNode(value);
        newNode.right = this.right;
        newNode.left = this.left.right;
        this.setValue(left.value);
        this.setRight(newNode);
        this.setLeft(this.left.left);
    }

4 双旋

image-20230109120404116.png

  1. 问题:前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。比如数列

    int[] arr = { 10, 11, 7, 6, 8, 9 }; 运行原来的代码可以看到,并没有转成 AVL 树.

    int[] arr = {2,1,6,5,7,3}; // 运行原来的代码可以看到,并没有转成 AVL 树

  2. 解决如下:

  3. 当符合右旋转的条件时,如果它的左子树的右子树高度大于它的左子树的高度,先对当前这个结点的左节点进行左旋转,再对当前结点进行右旋转的操作即可

  4. 当符合左旋转的条件时,如果它的右子树的左子树高度大于它的右子树的高度,先对当前这个结点的右节点进行右旋转,再对当前结点进行左旋转的操作即可

//添加节点
    public void add(BNode bnode) {
        if (bnode == null) {
            return;
        }
        if (bnode.value < this.value) {
            if (this.left == null) {
                this.left = bnode;
            } else {
                this.left.add(bnode);
            }
        } else {
            if (this.right == null) {
                this.right = bnode;
            } else {
                this.right.add(bnode);
            }
        }

        //考虑左旋
//        if (rightTreeHeight() - leftTreeHeight() > 1) {
//            leftRotate();
//        }
        //考虑右旋
//        if (leftTreeHeight()-rightTreeHeight() > 1) {
//            rightRotate();
//        }
        //考虑双旋(单独的左旋/右旋可能不能解决问题)
        if (rightTreeHeight() - leftTreeHeight() > 1) {
            if (right != null && right.leftTreeHeight() > right.rightTreeHeight()) {
                right.rightRotate();
            }
            leftRotate();
            return;//省的走下面代码,影响效率(因为涉及递归调用)
        }
        if (leftTreeHeight()-rightTreeHeight() > 1) {
            if (left != null && left.leftTreeHeight() < left.rightTreeHeight()) {
                left.leftRotate();
            }
            rightRotate();
        }
    }

5 完整AVL树代码

//平衡二叉树
public class App05_AVLTree {
    public static void main(String[] args) {
        AVLTree tree = new AVLTree();
//        int[] arr = {4,3,6,5,7,8};
//        int[] arr = {10,12, 8, 9, 7, 6};
//        int[] arr = {10, 11, 7, 6, 8, 9};
        int[] arr = {2,1,6,5,7,3};
        for (int i : arr) {
            tree.add(new BNode(i));
        }
        tree.infixOrder();
        System.out.println("根高度:"+tree.getRoot().getHeight());
        System.out.println("左子树高度:"+tree.getRoot().leftTreeHeight());
        System.out.println("右子树高度:"+tree.getRoot().rightTreeHeight());
    }
}

class AVLTree {
    private BNode root;

    public BNode getRoot() {
        return root;
    }

    //中序遍历
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("树为空!!!");
        }
    }

    //添加节点
    public void add(BNode nd) {
        if (root == null) {
            root = nd;
        } else {
            root.add(nd);
        }
    }

    //找到删除节点及其父节点
    public BNode search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }
    public BNode searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            if (root.getLeft() == null && root.getRight() == null) {
                return null;
            } else {
                return root.searchParent(value);
            }
        }
    }

    //删除节点
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            BNode target = search(value);
            BNode parent = searchParent(value);//为空则只能是删根节点
            //有的删才行
            if (target != null) {
                if (parent != null) {
                    //删叶子节点
                    if (target.getLeft() == null && target.getRight() == null) {
                        if (parent.getLeft()!=null&&parent.getLeft().getValue() == value) {
                            parent.setLeft(null);
                        } else {
                            parent.setRight(null);
                        }

                        //删有双子树的节点
                    } else if (target.getLeft() != null && target.getRight() != null) {
                        //target为左子树
                        if (parent.getLeft().getValue() == value) {
                            int max = delLeftTreeMax(target);
                            target.setValue(max);
                            //target为右子树
                        } else {
                            int min = delRightTreeMin(target);
                            target.setValue(min);
                        }

                        //删有单子树的节点
                    } else {
                        //4种可能,左左,左右,右左,右右
                        if (parent.getLeft()!=null&& parent.getLeft().getValue() == value && target.getLeft() != null) {
                            parent.setLeft(target.getLeft());
                        } else if (parent.getLeft() != null && parent.getLeft().getValue() == value && target.getRight() != null) {
                            parent.setLeft(target.getRight());
                        } else if (parent.getRight().getValue() == value && target.getLeft() != null) {
                            parent.setRight(target.getLeft());
                        } else {
                            parent.setRight(target.getRight());
                        }
                    }
                } else {//没有父节点,说明就是删root,因为删除的节点存在
                    //叶子
                    if (root.getLeft() == null && root.getRight() == null) {
                        root = null;
                        //有双子树
                    } else if (root.getLeft() != null && root.getRight() != null) {
                        //用左子树最大或右子树最小都可以
                        root.setValue(delLeftTreeMax(root.getLeft()));

                        //有单子树
                    } else {
                        if (root.getRight() != null) {
                            root = root.getRight();
                        } else {
                            root = root.getLeft();
                        }
                    }
                }
            } else {
                System.out.println("删除的节点不存在!!!");
            }
        }
    }

    //删左子树,最大值
    public int delLeftTreeMax(BNode nd) {
        BNode temp = nd;
        while (true) {
            if (temp.getRight() == null) {
                break;
            }
            temp = temp.getRight();
        }
        delNode(temp.getValue());
        return temp.getValue();
    }
    //删右子树,最小值
    public int delRightTreeMin(BNode nd) {
        BNode temp = nd;
        while (true) {
            if (temp.getLeft() == null) {
                break;
            }
            temp = temp.getLeft();
        }
        delNode(temp.getValue());
        return temp.getValue();
    }
}

class BNode {
    private int value;
    private BNode left;
    private BNode right;

    public BNode(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public BNode getLeft() {
        return left;
    }

    public void setLeft(BNode left) {
        this.left = left;
    }

    public BNode getRight() {
        return right;
    }

    public void setRight(BNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "BNode [value=" + value + "]";
    }

    //中序遍历
    public void infixOrder() {
        if (this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    //添加节点
    public void add(BNode bnode) {
        if (bnode == null) {
            return;
        }
        if (bnode.value < this.value) {
            if (this.left == null) {
                this.left = bnode;
            } else {
                this.left.add(bnode);
            }
        } else {
            if (this.right == null) {
                this.right = bnode;
            } else {
                this.right.add(bnode);
            }
        }

        //考虑左旋
//        if (rightTreeHeight() - leftTreeHeight() > 1) {
//            leftRotate();
//        }
        //考虑右旋
//        if (leftTreeHeight()-rightTreeHeight() > 1) {
//            rightRotate();
//        }
        //考虑双旋(单独的左旋/右旋可能不能解决问题)
        if (rightTreeHeight() - leftTreeHeight() > 1) {
            if (right != null && right.leftTreeHeight() > right.rightTreeHeight()) {
                right.rightRotate();
            }
            leftRotate();
            return;//省的走下面代码,影响效率(因为涉及递归调用)
        }
        if (leftTreeHeight()-rightTreeHeight() > 1) {
            if (left != null && left.leftTreeHeight() < left.rightTreeHeight()) {
                left.leftRotate();
            }
            rightRotate();
        }
    }

    //查找要删除节点
    public BNode search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {//这里对于==的处理与add方法保持一致
            if (this.left != null) {
                return this.left.search(value);
            }
        } else {
            if (this.right != null) {
                return this.right.search(value);
            }
        }
        return null;
    }

    //查找要删除的节点的父节点
    public BNode searchParent(int value) {
        if ((this.left != null && this.left.value == value) ||
                this.right != null && this.right.value == value) {
            return this;
        } else {
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            } else {
                return null;
            }
        }
    }

    //返回当前节点的高度
    public int getHeight() {
        return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight())+1;
    }
    //左子树高度
    public int leftTreeHeight() {
        if (left == null) {
            return 0;
        } else {
            return left.getHeight();
        }
    }
    //右子树高度
    public int rightTreeHeight() {
        if (right == null) {
            return 0;
        } else {
            return right.getHeight();
        }
    }

    //左旋
    public void leftRotate() {
        BNode newNode = new BNode(value);
        newNode.left = this.left;
        newNode.right = this.right.left;
        this.setValue(right.value);
        this.setLeft(newNode);
        this.setRight(this.right.right);
    }
    //右旋
    public void rightRotate() {
        BNode newNode = new BNode(value);
        newNode.right = this.right;
        newNode.left = this.left.right;
        this.setValue(left.value);
        this.setRight(newNode);
        this.setLeft(this.left.left);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值