【数据结构进阶】二叉平衡树

导读:本篇文章讲解 【数据结构进阶】二叉平衡树,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、 二叉平衡树

概念

二叉搜索树有称 二叉排序树,它也可以是一个空树。

  • 如果它的左子树不为空,则左子树上所有结点的值都小于根结点的值
  • 如果他的右子树不为空,则右子树上所有结点的值都大于根结点的值
  • 它的左右子树也分别是二叉搜索树
    在这里插入图片描述

由图可以看出:二叉搜索树中最左侧的节点是树中最小的节点,最右侧节点一定是树中最大的节点

在这里插入图片描述

采用中序遍历遍历二叉搜索树,可以得到一个有序的序列:

1 3 4 5 6 7 8 9 10

二叉搜索树的查找

若根结点不为空:
	如果根结点的值==target值 返回true
	如果根结点的值 > target值 在其左子树查找
	如果根结点的值 < target值 在其右子树查找
否则 返回false	

二叉树查询效率【性能分析】

最优情况:二叉搜索树为完全二叉树,其平均比较次数 在这里插入图片描述

最差情况:二叉搜索树退化成单支树,其平均比较次数在这里插入图片描述

二、 AVL树【高度平衡的二叉搜索树】

概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过
1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

下面说一下二叉搜索树的特点:

  • 它的左子树右子树都是AVL树。
  • 左右子树高度之差的绝对值不超过 1

在这里插入图片描述

如果一颗二叉搜索树是高度平衡的,他就是AVL树。它有n个结点,其高度可保持在 O(log2N),搜索时间复杂度O( log2N)

AVL树结点的定义

为了AVL树实现简单,AVL树节点在定义时维护一个平衡因子,具体节点定义如下:
在这里插入图片描述

static class TreeNode {
        public int val;
        public int bf; //平衡因子
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;

        public TreeNode(int val) {
            this.val = val;
        }
    }

AVL树的插入

public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val == val) {
                return false;
            } else {
                parent = cur;
                cur = cur.left;
            }
        }
        //cur == null
        if (parent.val < val) {
            parent.right = node;
        } else {
            parent.left = node;
        }
        //以上是将数据插入到AVL树中 【和二叉搜索树一样】
        //平衡因子 = 右子树高度 - 左子树高度
        node.parent = parent;
        //插入之后,根据平衡因子来进行树的调整
        cur = node;
        //调节平衡因子
        while (parent!=null) {
            //先看cur是parent的左还是右 决定平衡因子是++ 还是 --
            if (cur == parent.right) {
                parent.bf++;
            } else {
                parent.bf--;
            }
            //检查当前的平衡因子 是不是绝对值 1 0 -1
            if (parent.bf == 0) {
                //说明平衡
                break;
            } else if (parent.bf == 1 || parent.bf == -1) {
                //1 和 -1 代表当前分支树平衡 不代表整个树平衡
//                继续向上判断 修改平衡因子
                cur = parent;
                parent = cur.parent;

            } else {
                if (parent.bf == 2) {
//                    parent.bf == 2 右树高 需要降低右树的高度

                    if (cur.bf == 1) {
                        rotateLeft(parent);

                    } else {
//                        parent.bf == -1
                        //右左双旋

                    }
                } else {
//                    parent.bf == -2 左树高 需要降低左树的高度
                    if (cur.bf == -1) {
                        //右旋
                        rotateRight(parent);
                    } else {
//                        parent.bf == 1
                        //左右双旋
                        rotateLR(parent);

                    }
                }
                break;
            }
        }
        return false;
    }

AVL树的旋转

右单旋
在这里插入图片描述

左单旋
在这里插入图片描述

左右双旋

在这里插入图片描述

右左双旋

在这里插入图片描述

旋转代码:

   //右单旋
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        if (subLR != null) {
            subLR.parent = parent;
        }
        subL.right = parent;
//记录下当前parent的父亲
        TreeNode parParent = parent.parent;
        parent.parent = subL;
        if (parent == root) {
        //独立的子树
            root = subL;
            root.parent = null;//subL.parent = null;
        } else {
//不是独立的子树,是其他树的左树或者右树
            if (parParent.left == parent) {
//                2. 新节点插入较高右子树的右侧---左单旋
//                左单旋的实现,学生们可以参考右单旋的实现。
                parParent.left = subL;
            } else {
                parParent.right = subL;
            }
            subL.parent = parParent;
        }
        subL.bf = 0;
        parent.bf = 0;

    }

    // 左单旋
    private void rotateLeft(TreeNode parent) {
        //subR为parent的右树
        TreeNode subR = parent.right;
        //subRL为subR的左树
        TreeNode subRL = subR.left;
        //1、第一个修改的指向
        parent.right = subRL;
        //2、第2个修改的指向有可能subRL这棵树是空树
        if (subRL != null) {
            subRL.parent = parent;
        }
        //3、第2个修改的指向
        subR.left = parent;
         //记录下来 之前的parent的父亲
        TreeNode parentParent = parent.parent;
        //4、第4个修改的指向
        parent.parent = subR;
        //5、有可能当前父亲节点,不是根
        if (parent == root) {
            root = subR;
            root.parent = null;
        } else {
            if (parent == parentParent.left) {
                parentParent.left = subR;
                subR.parent = parentParent;
            } else {
                parentParent.right = subR;
                subR.parent = parentParent;
            }
        }
        subR.bf = parent.bf = 0;
    }
    //左右双旋
    private void rotateLR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        rotateLeft(parent.left);
        rotateRight(parent);
        if(bf == -1) {
            subL.bf = 0;
            parent.bf = 1;
            subLR.bf = 0;
        }else if(bf == 1) {
            subL.bf = -1;
            parent.bf = 0;
            subLR.bf = 0;
        }
    }

    //右左双旋
    private void rotateRL(TreeNode parent) {
       TreeNode subR = parent.right;
       TreeNode subRL = subR.left;
       int bf = subRL.bf;

       rotateRight(parent.right);
       rotateLeft(parent);

       if (bf == 1){
           parent.bf = -1;
           subR.bf = 0;
           subRL.bf = 0;
       }else if(bf == -1){
           parent.bf = 0;
           subR.bf = 0;
           subRL.bf = 1;
       }
    }

AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
    比特就业课
  2. 验证其为平衡树
  • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
  • 节点的平衡因子是否计算正确

验证当前树是不是 AVL树,如果是,该怎么验证? 【高度平衡二叉搜索树】

在这里插入图片描述

//中序遍历的结果是有序的 就能说明当前树 一定是AVL树吗? 不一定的
    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        System.out.println(root.val + " ");
        inorder(root.right);
    }
    private int height(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftH = height(root.left);
        int rigthH = height(root.right);

        return leftH>rigthH?leftH+1:rigthH+1;
    }
    private boolean isBalance(TreeNode root) {
        if(root == null) {
            return true;
        }
        int leftH = height(root.left);
        int rightH = height(root.right);
        //检查平衡因子
        if(rightH - leftH != root.bf) {
            System.out.println(root.val+" 平衡因子异常!");
            return false;
        }
        return Math.abs(leftH-rightH) <= 1 && isBalance(root.left) && isBalance(root.right);
    }

测试:
在这里插入图片描述

AVL树的删除

删除步骤:

  • 找到需要删除的结点
  • 按照搜索二叉树的删除规则删除结点
  • 更新平衡因子,如果出现不平衡,进行旋转 – 单旋/双旋

AVL树的查找效率【性能分析】

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即在这里插入图片描述

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/119513.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!