• AVL树插入操作InsertAVL的实现


    AVL树是非常重要的一种数据结构,这里实现了在AVL树中的插入操作,包括插入后整个树的自平衡。

    这里有几点值得注意的地方:

    1).左旋L_Rotate与右旋R_Rotate操作:

    这两个操作传递进来的参数是以TreeNode*&的形式传递进来的,也就是说传递的是指针的引用,效果等价于传递二级指针

    如果不加入&,则在函数内部更改的是形参的指向,因为实际上函数调用时,如果不采用引用传递,则会构造一个与原T指向同一个地方的临时变量指针,在X_Rotate的内部也是对这个临时变量进行操作,等到返回后对原来的T一点影响都没有。因此对于指针的操作,如果说需要在某个函数内更改这个指针的指向,则要么传递二级指针,要么传递指针的引用。

    2).在LR型或RL型中:

    以LR型为例,要根据不平衡点的左子树的根的右孩子rd的bf值来确定T与lc的bf值,其中rd->bf会出现等于0的情况。

    这种情况只会出现在rc才是新插入的节点,也就是说lc->right在原来未插入时是NULL,只有在这种情况下才会显现在rc->bf=0,树却增高的情况。

    一点小小的总结:

    AVL树第一次接触感觉很复杂,转来转去,四个形状,其实思考清楚后整个思路还是很简单的:

    首先是LL型与RR型:

    这两种情况是最简单的,只需要简单的右旋/左旋即可.

    以LL型为例子:对T右旋后,实际上就是将T->left->right接到T的left上,并将T->left->right改为接上T。

    RR型也是如此。

    然后是LR型与RL型:

    这两种情况复杂的原因在于,仅仅是右旋/左旋,T的bf仍不会变化。

    以LR型为例子:

    问题出现在右旋后将T->left->right接到T的left并不会改变T的bf。

    但实际上LR型可以看做这样一个过程:先将他转换为LL型。

    也就是说,如果把插入节点插入到T->left的左子树上,就转变为第一个问题了。

    现在的问题在于怎么把插入在T->left的右子树上的节点看做插在左子树上呢,只要我们对T->left进行一次左旋。

    我们现在将T->left看做一个树的根节点,对这个T’进行一次左旋,根据左旋的规则发现原本插在T'->right上的节点现在被旋转到T'->right上了,而T->left的位置现在被T'->left->right所占据,现在的问题变为了LL型,则只要想问题一一样进行一次右旋就可以了。

    这也就是LR型的整个过程。

    对RL型也是如此。

    struct TreeNode
    {
    	int val;
    	int bf;
    	struct TreeNode* left;
    	struct TreeNode* right;
    };
    class Solution
    {
    public:
    	int InsertAVL(TreeNode*& T,int val,bool& taller)
    	{
    		if(!T)
    		{
    			T=new TreeNode;
    			T->bf=0;
    			T->left=T->right=NULL;
    			T->val=val;
    			taller=true;
    		}
    		else
    		{
    			if(val==T->val)
    			{
    				taller=false;
    				return 0;
    			}
    			else if(val<T->val)//left
    			{
    				if(!InsertAVL(T->left,val,taller))
    					return 0;
    				if(taller)
    				{
    					switch(T->bf)
    					{
    					case -1:
    						taller=false;T->bf=0;break;
    					case 0:
    						taller=true;T->bf=1;break;
    					case 1:
    						LeftBalance(T);taller=false;break;
    					}
    				}
    			}
    			else
    			{
    				if(!InsertAVL(T->right,val,taller))
    					return 0;
    				if(taller)
    				{
    					switch(T->bf)
    					{
    					case 1:
    						taller=false;T->bf=0;break;
    					case 0:
    						taller=true;T->bf=-1;break;
    					case -1:
    						RightBalance(T);taller=false;break;
    					}
    				}
    			}
    		}
    		return 1;
    	}
    private:
    	void R_Rotate(TreeNode*& p)//注意这里是传递的指针的引用,效果等价于传递二级指针
    	{
    		TreeNode* lc=p->left;
    		p->left=lc->right;
    		lc->right=p;
    		p=lc;
    	}
    	void L_Rotate(TreeNode*& p)
    	{
    		TreeNode* rc=p->right;
    		p->right=rc->left;
    		rc->left=p;
    		p=rc;
    	}
    	void LeftBalance(TreeNode*& T)
    	{
    		TreeNode* lc=T->left;
    		switch(lc->bf)
    		{
    		case 1:
    			T->bf=lc->bf=0;R_Rotate(T);break;
    		case -1:
    			TreeNode* rd=lc->right;
    			switch(rd->bf)
    			{
    			case 1:
    				lc->bf=0;T->bf=-1;break;
    			case 0://注意这里rd->bf=0.这种情况是rd这个节点就是新插入的节点,它的左右子树都为空,所以rd->bf=0.只有这种情况才会出现rd->bf=0
    				lc->bf=T->bf=0;break;
    			case -1:
    				lc->bf=1;T->bf=0;break;
    			}
    			rd->bf=0;
    			L_Rotate(T->left);
    			R_Rotate(T);
    			break;
    		}
    	}
    	void RightBalance(TreeNode*& T)
    	{
    		TreeNode* rc=T->right;
    		switch(rc->bf)
    		{
    		case -1:
    			rc->bf=T->bf=0;break;
    		case 1:
    			TreeNode* ld=rc->left;
    			switch(ld->bf)
    			{
    			case -1:
    				rc->bf=0;T->bf=1;break;
    			case 0:
    				rc->bf=T->bf=0;break;
    			case 1:
    				rc->bf=-1;T->bf=0;break;
    			}
    			ld->bf=0;
    			R_Rotate(T->right);
    			L_Rotate(T);
    			break;
    		}
    	}
    };
    

      

  • 相关阅读:
    在实体属性上通过注解格式化日期
    @Validated和@Valid区别:Spring validation验证框架对入参实体进行嵌套验证必须在相应属性(字段)加上@Valid而不是@Validated
    两种根据关键字查询的方法SQL
    excel批量导入数据
    下载excel模板
    上传人员照片
    身份证校验类
    把字符串参数分割成数组 传入SQL foreach遍历查询
    使用Hibernate-Validator优雅的校验参数
    如何使用Graphics2D在一张图片上画线(包括箭头)
  • 原文地址:https://www.cnblogs.com/lxy-xf/p/11311851.html
Copyright © 2020-2023  润新知