AVL树中各种旋转情况分析

1,142 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看8月更文挑战

概览

    AVL树其实是在二叉搜索树上演变而来。在此我们简短地对二叉搜索树的相关概念进行一个简短地回顾。      

对于二叉搜索树我们有如下的定义

  • 任意一个节点的值都大于其左子树所有节点的值
    
  • 任意一个节点的值都小于其右子树所有节点的值
    

   然后我们采取递归的方式进行定义,此时要求树中所有的左右子树也是一棵二又搜索树。图图所示就是一个典型的二叉搜索树

           image.png

平衡二叉树所面临的问题:

    二叉搜索树最大的特点就是他在最优情况系可以实现 O()复杂度内完成信息的查询操作,但是最坏情况下他可能会面临如下的情况:

    如果给定的数据信息以:2,4,5,7,8,9,11的形式给出,此时便树便退化成单链表的形式,使得查询效率降低,所以就需要有一种机制来尽量避免树退化成单链表,此时AVL树的概念被提出。

    

      image.png

    AVL树在原有的二叉搜索树的基础上增添一个新的限制:即每个节点的左右子树高度差绝对值不超过1。

   当高度差超过1时,我们就需要进行旋转操作,以此来避免搜索树退化为单链表的形式。一般来看导致失衡的情况主要有LL,LR,RR,RL等几种情况。

旋转操作

        在分析调整之前我们首先要区分清楚一个概念:我们常说的RR和LL说的是树失衡时的情况,而非旋转情况。很多人容易将失衡情况和旋转情况搞混,以为当发生LL不平衡现象时进行左旋转,这其实是错误的观点。 

   文末会对整体的旋转情况进行总结回顾,以加深你对AVL树旋转的记忆。

各种不平衡情况

LL 情况

   当我们在一个节点的左子树的左子树上插入一个新节点使得树发生失衡,此种情况称为LL。我们需要进行右旋转就能使其平衡。

     image.png

  当LL情况发生时我们要通过右旋转来使得搜索树重新达到平衡。这个旋转的过程具体如下:

        image.png

通过图我们很容易就可以看出对于LL失衡条件下的右旋调整情况主要有如下几个关键步骤:

1.将失衡节点的左指针指向当前节点左子树的右子树节点 。

如图所示:当前失衡节点为A,此时调整时首先将其左指针指向B节点的右子树信息\color{blue}如图所示:当前失衡节点为A,此时调整时首先将其左指针指向B节点的右子树信息

  2. 将当前失衡节点的左子树提升为父节点,失衡节点作为其右子树节点。

  如图所示:A的左子树B提升为父节点,调整A作为B的右子树节点信息。\color{blue}如图所示:A的左子树B提升为父节点,调整A作为B的右子树节点信息。

RR情况

  当我们在一个节点的右子树的右子树上插入一个新节点使得树发生失衡。此种情况称为LL。我们需要进行左旋转就能使其平衡

RR情况的旋转

image.png RR时左旋操作

image.png   调整过程:

1.将失衡节点的右指针指向当前节点右子树的左子树节点 。

   如图所示:当前失衡节点为A,此时调整时首先将其指针指向B节点的左子树信息\color{green}如图所示:当前失衡节点为A,此时调整时首先将其→指针指向B节点的左子树信息

 2. 将当前失衡节点的右子树提升为父节点,失衡节点作为其左子树节点。

      如图所示:A的右子树B提升为父节点,调整A作为B的左子树节点信息。\color{green}如图所示:A的右子树B提升为父节点,调整A作为B的左子树节点信息。

 经过上述分析后我们明白了左旋 和右旋的具体操作细节,这是处理 LR,RL情况时所必须的技巧。

LR情况

   当我们在一个节点左子树的右子树上插入节点而使得失衡时,我们称这种情况为LR失衡

image.png

    这种情况下我们需要旋转两次,首先经过一个右旋,然后经过一个左旋。

RL情况

 当我们在一个节点右子树的左子树上插入节点而使得失衡时,我们称这种情况为RL失衡。

image.png

  这种情况下我们需要旋转两次,首先经过一个左旋,然后经过一个右旋

示例代码

/**
* Definition for a binary tree node.
* public class TreeNode {
*     int val;
*     TreeNode left;
*     TreeNode right;
*     TreeNode() {}
*     TreeNode(int val) { this.val = val; }
*     TreeNode(int val, TreeNode left, TreeNode right) {
*         this.val = val;
*         this.left = left;
*         this.right = right;
*     }
* }
*/
public class Solution{
    // LL情况下的旋转
    private TreeNode leftleft(TreeNode root) {
       TreeNode newRoot = root.left;
       root.left = newRoot.right;
       newRoot.right = root;
       return newRoot;
   }
   //RR情况下的旋转
   private TreeNode rightright(TreeNode root) {
       TreeNode newRoot = root.right;
       root.right = newRoot.left;
       newRoot.left = root;
       return newRoot;
   }
   // LR下情况
   private TreeNode leftRight(TreeNode root) {
       root.left = rightright(root.left);
       return leftleft(root);
   }
   // RL情况
   private  TreeNode rightleft(TreeNode root) {
       root.right = leftleft(root.right);
       return rightright(root);
   }
}