平衡树总结

**

平衡树总结

**
从大一下开始了解AVL树一直对于平衡树的旋转不甚了解,中间放弃了很长时间,最近结合着数据结构课上对平衡树的讲解,又从新开始了对AVL树的学习,并在最后得到了比较令我满意的结果,放图纪念在这里插入图片描述下面是我在学习AVL树时的一些见解,如有错误还请神犇在评论区指出,在此先感谢神犇了。

AVL树是指每一个非叶子节点的子树的高度差不超过1的树,在这里我们建立一颗二叉平衡检索树为例。

下面的说法纯属个人见解!!!!

  1. 个人认为AVL树的难点在于非平衡节点的旋转即:rr,rl,ll,lr旋转。
  2. AVL树除了多了旋转操作以外并没有于正常的树有任何不同。
    由于把树创建完成之后再对其进行旋转比较复杂并且可能的操作过多,没有办法直接找到最优的解决方案,所以在此我决定在插入的时候判断树是否平衡,进而直接旋转,保证每次插入的树都是一颗平衡树,由此可以保证非平衡节点的平衡因子均为2或-2。在这里我们只讨论RR和RL旋转,LL和LR类比即可。

下面是我对AVL树的旋转操作的一些理解:
3. 要执行旋转操作,首先要明白在该情况下应该执行什么操作,为方便理解在这里放一些图。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述上面所绘的四幅图均是主根节点非平衡,且非平衡因子均为2,所以可以判断要进行R*旋转操作。紧接着观察非平衡节点的右子树发现前两者的平衡因子为-1 后两者的平衡因子为1所以可以判定前两者的旋转方式为RR而后两者的旋转方式为RL。(也许有人认为应该观察最后一个节点插在左子树还是右子树上来区别RR和RL,在这里不能苟同,有反例欢迎在评论区指出)。

  1. 下面我们来具体的谈论一下RR旋转的操作。
    为方便理解,在这里先放张图
    在这里插入图片描述这个图是上面图1的精简版可以很清楚的看出RR旋转操作的本质—将中间节点上提以达到减少树高的目的。具体操作步骤为1:备份1号节点并用2号节点覆盖1号节点,2:把2号节点的左子树赋值为已备份的一号节点。
    看起来很简单,但实际上有一个问题,如果原本的2号节点有左儿子。上面的图1便是个很好的例子,在这里因为要考虑到二叉检索树的性质(根比左儿子大,比右儿子小)并为了使算法不过分啰嗦,我给出的解决方案是

在这里插入图片描述左面看不清看上面图一吧…

如图:依然是将中间的c节点上提,原本是C节点的左子树的D节点变成了A节点的右子树。这样写保证了二叉检索树的性质。同时因为C节点上提A的右子树一定为空。所以将D节点放在A的右子树上合情合理。所以完整的RR旋转的步骤1:备份根节点和根的右儿子的左儿子节点并用根的右儿子覆盖根节点,2:把覆盖过的根节点的左子树赋值为已备份的前根节点。3:把前根节点的右儿子赋值为已备份的根的右儿子的左儿子节点(好拗口)。
下面是具体的代码实现

void rr(node *&root)     //root 指的是非平衡节点即上图的A节点
{
	node *t=root;
	root=root->rson;
	node *tt=root->lson;
	root->lson=t;
	root->lson->rson=tt;
	root->lson->r_h=root->l_h;                                  //**********
	//	root->l_h=max(root->lson->l_h,root->lson->r_h)+1;       
	root->l_h=root->r_h;										//**********
}

其中root->l_h=root->r_h;这句是我观察出的规律….即在原本是平衡树上插入一个节点导致不平衡再进行RR旋转操作之后原本的非平衡节点的平衡因子一定为0。

打星的两句目的是更新平衡因子,一定要记得鸭T_T(鬼知道我找了多少时间BUG….)。
3. 下面我们来具体的谈论一下RL旋转的操作
同样为方便理解先上图
在这里插入图片描述
这个图是上面图3的精简版可以很清楚的看出RR旋转操作的本质—将这种难以处理的情况化为上面已经处理过的RR旋转可解的情况。具体操作为1:备份3号节点,并用二号节点覆盖三号节点2: 将二号节点的右子树赋值为已备份的3号节点.

和RR旋转一样这种操作也有一个特殊情况即2号节点存在右子树的情况,就是上图3的情况同样,为了考虑到二叉检索树的性质我们做以下处理

在这里插入图片描述
左面看不清的看上图图三
然后再对A节点做一次RR旋转即可.
具体步骤类比RR旋转

void rl(node * &root) //root为非平衡节点的右儿子
{
	node *t=root;
	node *tt=root->lson->rson; 
	root=root->lson;
	root->rson=t;
	root->rson->lson=tt;
	root->rson->l_h=root->r_h;					  //*******************
	root->r_h=max(root->rson->l_h,root->rson->r_h)+1;//*******************
}

同样的…别忘记打星的几句…

下面是完整的代码

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdlib>
#include <time.h>
using namespace std;

struct node{
	int date;
	int c;
	int l_h;
	int r_h;
	node *lson;
	node *rson;
	node(int _date=0,int _c=0)
	{
		lson=NULL;
		rson=NULL;
		date=_date;
		c=_c;
		l_h=0;
		r_h=0;
	}
};

void rr(node *&root)
{
	node *t=root;
	root=root->rson;
	node *tt=root->lson;
	root->lson=t;
	root->lson->rson=tt;
	root->lson->r_h=root->l_h;                                  //**********
	//	root->l_h=max(root->lson->l_h,root->lson->r_h)+1;       
	root->l_h=root->r_h;										//**********
}

void rl(node * &root)
{
	node *t=root;
	node *tt=root->lson->rson; 
	root=root->lson;
	root->rson=t;
	root->rson->lson=tt;
	root->rson->l_h=root->r_h;						 //*******************
	root->r_h=max(root->rson->l_h,root->rson->r_h)+1;//*******************
}

void ll(node *&root)
{
	node *t=root;
	node *tt=root->lson->rson;
	root=root->lson;
	root->rson=t;
	root->rson->lson=tt;
	root->rson->l_h=root->r_h;    //*************
	root->r_h=root->l_h;          //*************
}

void lr(node *&root)
{
	node *t=root;
	node *tt=root->rson->lson;
	root=root->rson;
	root->lson=t;
	root->lson->rson=tt;
	root->lson->r_h=root->l_h;
	root->l_h=max(root->lson->l_h,root->lson->r_h)+1;
}

int search(node *root)
{
	if(root->l_h-root->r_h==-2)
	{
		if(root->rson->l_h-root->rson->r_h<0)
		return 1;
		else
		return 2;
	}
	else if(root->l_h-root->r_h==2)
	{
		if(root->lson->l_h - root->lson->r_h>0)
		return 3;
		else
		return 4;
	}
	return 0;
}
void show(node *root)
{
	queue<node *> que;
	que.push(root);
	while(!que.empty())
	{
		node *t=que.front();
		cout<<t->date<<" l_h:"<<t->l_h<<" r_h:"<<t->r_h<<endl;
		que.pop();
		if(t->lson!=NULL)
		que.push(t->lson);
		if(t->rson!=NULL)
		que.push(t->rson);
	}
}

void insert(node* &root,int e,int _c=0)
{
	if(root==NULL)
	{
		root=new node(e,_c);
		return ;
	}
	if(e>root->date)
	{
		insert(root->rson,e,_c+1);
		root->r_h=max(root->rson->l_h,root->rson->r_h)+1;
		int k;
		while(k=search(root))
		{
			if(k==1)
			rr(root);
			else if(k==2)
			rl(root->rson);
			else if(k==3)
			ll(root);
			else
			lr(root->lson);
		}
	}
	else if(e<root->date)
	{
		insert(root->lson,e,_c+1);
		root->l_h=max(root->lson->l_h,root->lson->r_h)+1;
		int k;
		while(k=search(root))
		{
			if(k==1)
			rr(root);
			else if(k==2)
			rl(root);
			else if(k==3)
			ll(root);
			else
			lr(root->lson);
		}
	}
	else
	{
		cout<<"error: have the same date"<<endl;
		return ;
	}
}

void z_show(node *root) 
{
	if(root!=NULL)
	{
		if(root->lson!=NULL)
		z_show(root->lson);
		cout<<root->date<<" ";
		if(root->rson!=NULL)
		z_show(root->rson);
	}
}
int main()
{
	srand((unsigned)time(NULL));
	node *head=NULL;
	int n=1000;
	while(n--)
	{
		insert(head,rand()%4000);
	}
	show(head);
	z_show(head);
	return 0;
}

感谢观看

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值