• AVL树


    AVL树这样一棵搜索二叉树,它的左右子树的深度之差不超过1。因此,他是带有条件的搜索二叉树。这个条件保证了AVL树的深度是O(log n).最简单的想法是左右两棵子树保持相同的高度。但是这种条件过于苛刻,难以使用。AVL只要求深度之差不超过1。AVL解决了二叉搜索树带来的不平衡问题。但是要求变成了我们必须在每次操作后进行调整,以使得AVL树保持平衡。另一种较新的方法是放弃平衡条件,允许树有任意的深度,但是在每次操作后要进行调整,以使得后面的操作效率更高。有一种这样的树称之为伸展树。

    在AVL树的每一个节点中保留其高度信息是必须的。在一棵高度为h的AVL树中,最少节点数S(h) = S(h-1)+S(h-2)+1。对于h为0时,S(h)=1;h为2时,S(h)=2。这个函数与斐波那契数列密切相关。

    如果一个插入操作破坏了AVL树的平衡条件,那么这插入后形成的左右子树的高度之差最大是2。因此只会出现4种情形。

    1. 对节点的左儿子的左子树进行一次插入;
    2. 对节点的右儿子的右子树进行一次插入;
    3. 对节点的左儿子的右子树进行一次插入;
    4. 对节点的右儿子的左子树进行一次插入;

    上述4种情形之中,1和2是镜像对称的,3和4是镜像对称的。所以看起来只有两种情况,但是对于编程实现而言还是4种情形。对于1,2这样的插入操作,可以通过单旋转来完成;对于3,4这样的插入操作,需要通过稍微复杂的双旋转操作来完成。

    单旋转:插入之前的高度是和插入之后的高度是保持一致的。插入操作不仅仅是修改局部的变化,树的其余部分也必须知道这个变化。插入可能会导致多个节点的平衡被破坏,但是我们只需要修复距离这个插入节点最近的被破坏平衡的节点。

    双旋转:对于1,2两种情形,如果采用单旋转来做,那么会发现,单旋转并没有降低树的深度。它还是不平衡的。双旋转解决了这个问题,它等价于做了两次单旋转。

    插入以后树可能还是平衡的,这时候不需要调整树的结构,但是我们仍然需要更新树的深度。

    代码实现如下

    AVL树的ADT:(avl.h文件)

    #ifndef AVLTREE
    #define AVLTREE
    
    #include<stdio.h>
    #include<stdlib.h>
    #define MAX(a,b) ((a) > (b) ? (a) : (b))
    
    typedef int ELementType;
    typedef struct AVLNode *Position;
    typedef Position AVLTree; /* AVL树类型 */
    
    struct AVLNode {
    	ELementType Data; /* 结点数据 */
    	AVLTree Left;     /* 指向左子树 */
    	AVLTree Right;    /* 指向右子树 */
    	int Height;       /* 树高 */
    };
    
    AVLTree Insert(ELementType x, AVLTree T);
    int Height(Position P);
    AVLTree LLRotate(Position K);
    AVLTree LRRotate(Position K);
    AVLTree RRRotate(Position K);
    AVLTree RLRotate(Position K);
    void InorderTraversal(AVLTree T);
    
    #endif // AVLTREE
    
    

    main.cpp文件:(测试流程)在二叉搜索树中已经实现了绝大多数的查找树操作,例如前序遍历,中序遍历,后序遍历,求最大,最小值等。在AVL树中就不一一实现了,只就插入做了实现,我对删除采用的是懒惰删除法。在此不在说明。只测试一下AVL树的深度是不是O(log n)以及中序遍历输出是不是有序的。这些足以证明它就是我们要求的AVL树。

    #include"avl.h"
    
    int main()
    {
    	AVLTree avltree = NULL;
    	printf("AVL树元素插入顺序如下:");
    	for (int i = 5; i > 0 ; i--)
    	{
    		printf("%d ",i);
    		avltree = Insert(i, avltree);
    	}
    	for (int i = 6; i < 10; i++)
    	{
    		printf("%d ", i);
    		avltree = Insert(i, avltree);
    	}
    	printf("
    中序遍历输出:");
    	InorderTraversal(avltree);
    	printf("
    AVL树的高度:%d
    ",Height(avltree));
    	system("pause");
    	return 0;
    }

    alv.cpp文件

    #include "avl.h"
    
    AVLTree Insert(ELementType x, AVLTree T)
    {
    	if (NULL == T)	//T为空,创建树
    	{
    		T = (AVLTree)malloc(sizeof(struct AVLNode));
    		if (NULL == T)
    		{
    			printf("创建树失败!");
    		}
    		else
    		{
    			T->Data = x;
    			T->Height = 0;		//树根深度为0
    			T->Left = NULL;
    			T->Right = NULL;
    		}
    	}
    	else
    	{
    		if (x < T->Data)		//x小于根节点数据
    		{
    			T->Left = Insert(x, T->Left);	//递归插入左子树
    			if (Height(T->Left) - Height(T->Right) == 2)	//AVL树不平衡了,调整一下
    			{
    				if (x < T->Left->Data)		
    				{
    					T = LLRotate(T);		//插入到左子树的左子树
    				}
    				else
    				{
    					T = LRRotate(T);		//插入到左子树的右子树
    				}
    			}
    		}
    		else if (x > T->Data)
    		{
    			T->Right = Insert(x, T->Right);		//递归插入右子树
    			if (Height(T->Right) - Height(T->Left) == 2)		//AVL树不平衡了,调整一下
    			{
    				if (x > T->Right->Data)
    				{
    					T = RRRotate(T);		//插入右子树的右子树
    				}
    				else
    				{
    					T = RLRotate(T);		//插入右子树的左子树
    				}
    			}
    		}
    		else
    		{
    			//x在这棵AVL树中,我们什么都不做,当然,我们也可以重新设计AVL树的ADT。
    			//使得ADT可以保存数据出现的次数,如果有相同数据插入,我们就使得次数加1。
    			//这样的做法为我们在AVL树中做一个删除也提供了一种方式,即:懒惰删除。
    			//我们并不将这个节点从树中删除,而只是去更改数据出现的次数减1。
    			//这样我们就不需要做多余的操作去调整可能出现的不平衡状态。
    			//这种做法在有重复关键字时很好用。并且这种做法只会导致树的高度略微上升。
    			//这样的做法使得删除变得非常快,并且如果被删除的元素重新插入,那么省去
    			//了重新申请空间的开销。
    		}
    		T->Height = MAX(Height(T->Left), Height(T->Right)) + 1;		//更新树的高度
    	}
    	return T;
    }
    
    int Height(Position P)		//AVL树的节点保存了高度这一信息,直接返回即可
    {
    	if (NULL == P)
    	{
    		return 0;		//节点为空,定义高度为0
    	}
    	else
    	{
    		return P->Height;
    	}
    }
    
    AVLTree LLRotate(Position K)
    {
    	Position M;
    	//调整AVL树
    	M = K->Left;
    	K->Left = K->Left->Right;
    	M->Right = K;
    
    	K->Height = MAX(Height(K->Left), Height(K->Right)) + 1;		//更新右子树的高度
    	M->Height = MAX(Height(M->Left), Height(M->Right)) + 1;		//更新新的根节点高度
    	//M->Height = MAX(Height(M->Left), K->Height) + 1;     Height(M-Right) == K->Height
    	
    	return M;
    }
    
    AVLTree LRRotate(Position K)
    {
    	//这里设计的很巧妙,通过先旋转K的左子树。然后在整体旋转K。
    	K->Left = RRRotate(K->Left);
    	return LLRotate(K);
    }
    
    AVLTree RRRotate(Position K)
    {
    	Position M;
    	//调整AVL树
    	M = K->Right;
    	K->Right = M->Left;
    	M->Left = K;
    
    	K->Height = MAX(Height(K->Left), Height(K->Right)) + 1;	//更新左子树的高度
    	M->Height = MAX(Height(M->Right), Height(M->Left)) + 1;	//更新新的根节点高度
    	//M->Height = MAX(Height(M->Left), K->Height) + 1;     Height(M-Right) == K->Height
    
    	return M;
    }
    
    AVLTree RLRotate(Position K)
    {
    	K->Right = LLRotate(K->Right);
    	return RRRotate(K);
    }
    
    void InorderTraversal(AVLTree T)
    {
    	if (T)
    	{
    		InorderTraversal(T->Left);
    		printf("%d ", T->Data);
    		InorderTraversal(T->Right);
    	}
    }
  • 相关阅读:
    HDU 5818 Joint Stacks
    HDU 5816 Hearthstone
    HDU 5812 Distance
    HDU 5807 Keep In Touch
    HDU 5798 Stabilization
    HDU 5543 Pick The Sticks
    Light OJ 1393 Crazy Calendar (尼姆博弈)
    NEFU 2016省赛演练一 I题 (模拟题)
    NEFU 2016省赛演练一 F题 (高精度加法)
    NEFU 2016省赛演练一 B题(递推)
  • 原文地址:https://www.cnblogs.com/zy666/p/10504286.html
Copyright © 2020-2023  润新知