zoukankan      html  css  js  c++  java
  • AVL树

    参考 skywang12345 http://www.cnblogs.com/skywang12345/p/3576969.html

    AVL树的介绍

    AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的。
    它是最先发明的自平衡二叉查找树,也被称为高度平衡树。相比于"二叉查找树",它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。

    上面的两张图片,左边的是AVL树,它的任何节点的两个子树的高度差别都<=1;而右边的不是AVL树,因为7的两颗子树的高度相差为2(以2为根节点的树的高度是3,而以8为根节点的树的高度是1)。

    AVL树的查找、插入和删除在平均和最坏情况下都是O(logn)。
    如果在AVL树中插入或删除节点后,使得高度之差大于1。此时,AVL树的平衡状态就被破坏,它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理。学AVL树,重点的地方也就是它的旋转算法;在后文的介绍中,再来对它进行详细介绍。

    AVL树的旋转

    前面说过,如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图:

    上图中的4棵树都是"失去平衡的AVL树",从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:


    上面的两张图都是为了便于理解,而列举的关于"失去平衡的AVL树"的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都由各自的定义:

    (1) LL:LeftLeft,也称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
         例如,在上面LL情况中,由于"根节点(8)的左子树(4)的左子树(2)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

    (2) LR:LeftRight,也称为"左右"。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
         例如,在上面LR情况中,由于"根节点(8)的左子树(4)的左子树(6)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

    (3) RL:RightLeft,称为"右左"。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。
         例如,在上面RL情况中,由于"根节点(8)的右子树(12)的左子树(10)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。

    (4) RR:RightRight,称为"右右"。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。

    LL旋转

    LL失去平衡的情况,可以通过一次旋转让AVL树恢复平衡。如下图:

    图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。
    对于LL旋转,你可以这样理解为:LL旋转是围绕"失去平衡的AVL根节点"进行的,也就是节点k2;而且由于是LL情况,即左左情况,就用手抓着"左孩子,即k1"使劲摇。将k1变成根节点,k2变成k1的右子树,"k1的右子树"变成"k2的左子树"。

    RR旋转

    理解了LL之后,RR就相当容易理解了。RR是与LL对称的情况!RR恢复平衡的旋转方法如下:

    图中左边是旋转之前的树,右边是旋转之后的树。RR旋转也只需要一次即可完成。

    LR旋转

    LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡。如下图:


    第一次旋转是围绕"k1"进行的"RR旋转",第二次是围绕"k3"进行的"LL旋转"。

    RL旋转

    RL是与LR的对称情况!RL恢复平衡的旋转方法如下:

    第一次旋转是围绕"k3"进行的"LL旋转",第二次是围绕"k1"进行的"RR旋转"。

    C++代码

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    struct TreeNode{
    	int key;
    	TreeNode* left;
    	TreeNode* right;
    	TreeNode(int _key, TreeNode* _left, TreeNode* _right) :key(_key), left(_left), right(_right){}
    };
    
    int getHeight(TreeNode* T){
    	if (T == NULL){
    		return 0;
    	}
    	return max(getHeight(T->left), getHeight(T->right)) + 1;
    }
    
    TreeNode* LL(TreeNode* T){
    	TreeNode* left = T->left;
    	T->left = left->right;
    	left->right = T;
    	return left;
    }
    
    
    TreeNode* RR(TreeNode* T){
    	TreeNode* right = T->right;
    	T->right = right->left;
    	right-> left = T;
    	return right;
    }
    
    TreeNode* LR(TreeNode* T){
    	T->left = RR(T->left);
    	return LL(T);
    }
    
    TreeNode* RL(TreeNode* T){
    	T->right = LL(T->right);
    	return RR(T);
    }
    
    TreeNode* insert(TreeNode* T, int val)
    {
    	if (T == nullptr)
    	{
    		T = new TreeNode(val,nullptr,nullptr);
    		return T;
    	}
    	
    	if (val < T->key)
    	{
    		T->left = insert(T->left, val);
    		if (getHeight(T->left) - getHeight(T->right) >= 2){
    			
    			if (val < T->left->key)
    				T = LL(T);  //左左旋转
    			else
    				T = LR(T); //左右旋转
    		}
    	}
    	else {
    		T->right = insert(T->right, val);
    		if (getHeight(T->right) - getHeight(T->left) >= 2){
    			
    			if (val > T->right->key)
    				T = RR(T); //右右旋转  
    			else
    				T = RL(T); //右左旋转
    		}
    	}
    
    	return T;
    }
    
    void preorder(TreeNode* T){
    	if (T == nullptr)
    		return;
    	cout << T->key << " ";
    	preorder(T->left);
    	preorder(T->right);
    }
    
    int main(void)
    {
    	TreeNode* T = nullptr;
    	for (size_t i = 0; i < 7; i++)
    		T = insert(T, i+1);
    	preorder(T);
    	
    }
    

      

  • 相关阅读:
    vgrant使用简易教程
    php数组常用函数总结
    php面向对象基础知识整理之类中的属性和方法的使用
    apache和nginx的区别
    thinkphp发送邮箱(以thinkphp5作为示例)。
    利用html2canvas将当前网页保存为图片.
    作为一名程序员该如何思考自己的职业人生?
    js常用返回网页顶部几种方法
    如何本地搭建centos7虚拟主机?
    Spark报错
  • 原文地址:https://www.cnblogs.com/willwu/p/6007585.html
Copyright © 2011-2022 走看看