平衡二叉树

//本篇文章只针对:对树有一定了解的读者,对树的属性有一定的认知。

//本篇文章只阐述思路,具体的代码实现,不讲解,用词不规范的地方请见谅。感兴趣的话可以下载Qt压缩包(C语言写个代码,还要用Qt验证,我也是惨)

节点树高:该节点的叶子节点(末梢)到该节点的最大节点个数。

        如下图的节点4:高度为3,左节点的最大节点数为2,右节点的最大节点个数也为2,故该节点高度为2+1为3。

        如下图的节点2:高度为2,左节点为1,右节点为0,因此该节点高度为1+1为2。

 平衡二叉树特性总结:选树上任一节点,该节点的的左节点高度与右节点高度差值不大于2。满足该特性的都为平衡二叉树。

 平衡调节:调整该节点的高度,重新找到该节点的中心节点,使新节点的左右节点高度相差不超过1。

        以上的根节点为4,现在要添加8值,插在7的右节点,从左到右为

3 - 4 -6 -7 - 8,但是此时的根节点为4,左节点的高度为1,右节点的高度为3,高度差大于1,因此需要调节。

        有两个方法:

        1、可以将 节点4所在的节点看成一个定滑轮,3 - 4(原中心) - 6 - 7- 8,看成一条绳子, 想象一下,滑轮右边的绳子长一点,那就拉一拉左边,因此3 -4被拉下去,7-8被提上来,中心节点变为了6,定滑轮处的节点位置也变为了6。3 - 4- 6(现中心) - 7- 8,此时已经平衡

        2、先不着急找到定滑轮的位置,先分析一下,都有哪些节点的左右节点高度差1以上,除了4外,还有6节点,这是离插入节点最近的不平衡节点。移动6节点试一下。将6节点看成定滑轮,6(原中心) - 7 -8 ,左点没有绳子,右边长,拉左边,则变成了6被拉下去,7,8上来,7变为中心节点,6 - 7(现中心) - 8。这样一来,4节点的左右节点的高度差就变为了1,因此该方法也就平衡了。

        分析一下:

        那我们写程序的时候应该采用哪种方法呢,本人的理解是这样的:

        先抛出结论,本人认为方法2才是程序的正确逻辑,先判断插入节点的父节点是否平衡,就这样一级一级判断、调整、返回,如此循环,直至判断出根节点平衡,这是有规律的。这是符合写程序的逻辑的。方法1是没有具体的逻辑指导的,当节点较多,一眼看不出如何移动的时候,请问你是否还可以随口说出如何移动吗???当时方法2可以。

        总结:调整不平衡节点时,应该是先从插入节点的最近不平衡节点开始调整,直至根节点平衡。

        可以配合着插入,简单看一下insert()的实现,可以暂时不需要关注平衡是如何具体实现的,只关注在哪一步需要平衡。因为平衡的实现又分为了两种实现方式:双旋与单旋

Node Insert(int x, Node Root)
{
	if(Root == NULL)
	{
        Root = (Node)malloc(sizeof(struct Avltree));
		
		Root->element = x;
		Root->Left = Root->Right = NULL;
		
		//Root->Hight = 0;
	}
	else if(x > Root->element)
	{
		//右插
		Root->Right = Insert(x,Root->Right);
		if(Hight(Root->Right) - Hight(Root->Left) == 2)
		{
			if(x > Root->Right->element)
				Root = SingleRotateLL(Root);
			else
				Root = DoubleRotateRL(Root);
		}
	}
	else if(x < Root->element)
	{
		//左插
		Root->Left = Insert(x,Root->Left);
		if(Hight(Root->Left) - Hight(Root->Right) == 2)
		{
			if(x < Root->Left->element)
				Root = SingleRotateRR(Root);
			else
				Root = DoubleRotateLR(Root);
		}
	}
	
    Root->Hight = max(Hight(Root->Left), Hight(Root->Right)) + 1;
	
	return Root;
}

平衡调节方法:旋转

方便描述:造个名词:

        次责节点:受被操作节点影响,导致原平衡节点变成最近不平衡节点的节点负次要责任,所以称为次责节点(不是最近不平衡节点的左节点,就是右节点)。

        主责节点:被操作节点 导致原平衡节点变成最近不平衡节点的节点,负主要责任。

单旋主责节点在次责节点的方向  与  次责节点在最近不平衡节点的方向一致,则只需旋转一次。

       将最近不平衡节点作为定滑轮的位置,那边短拉一下就好了,不平衡的节点就平衡了,也就是上面一直在说的。

双旋:方向不一致,需要旋转两次。先将次责节点作为定滑轮的位置,短的一边拉一下;再将最近不平衡节点作为定滑轮的位置,短的一边再拉一下,不平衡的节点自然平衡。

长短判断:往右边插入,肯定是右边的长度存在大于左边的可能啊,肯定不会出现左边大于右边。

                删除右边的元素,肯定是左边的长度存在大于右边的可能,肯定不会删了右边的元素,右边反而长了。

        这个时候在去看插入的代码,我想可能后更加的明了。

如果是从头看下来的话,如何插入,以及插入之后如何调整,到这里应该是很明白了吧。

插入:上面的描述有意无意都已经把插入讲明白了吧

删除:删除与插入其实是差不多的,列举一下删除是碰到的情况吧。

        1、要删除的节点没有左右子节点,此时节点直接干掉。

        2、要删除的节点只有一个子节点,将唯一的节点代替原节点,原节点删除。

        3、要删除的节点 左右节点都有,有两个方法,选一个

                将左节点上的最大值写入本节点,将左节点的最大值节点删除。

                将右节点上的最小值写入本节点,将右节点的最小值节点删除。

在删除时,遇到第3种情况,我们先判断一下,左边高还是右边高,然后在选择对应的方法删除。

与插入一样,每次删除完也需要调整一下,将其平衡。

BYTE Delete(int x, Node *pRoot)
{
	Node Root = pRoot[0];
	
	if( Root == NULL )
	{
		return FALSE;
	}
	else if(x < Root->element)
	{
		Delete(x, &Root->Left);
		if(Hight(Root->Right) - Hight(Root->Left) == 2)
		{
            if(Hight(Root->Right->Left) > Hight(Root->Right->Right))
			{
				pRoot[0] = DoubleRotateRL(Root);
			}
			else
			{
                pRoot[0] = SingleRotateLL(Root);
			}
		}
		
        pRoot[0]->Hight = max(Hight(pRoot[0]->Left), Hight(pRoot[0]->Right)) + 1;
	}
	else if(x > Root->element)
	{
		Delete(x, &Root->Right);
		if(Hight(Root->Left) - Hight(Root->Right) == 2)
		{
            if(Hight(Root->Left->Right) > Hight(Root->Left->Left))
			{
				pRoot[0] = DoubleRotateLR(Root);
			}
			else
			{
                pRoot[0] = SingleRotateRR(Root);
			}
		}
		
        pRoot[0]->Hight = max(Hight(pRoot[0]->Left), Hight(pRoot[0]->Right)) + 1;
	}
	else
	{
		if((Root->Left != NULL)&&(Root->Right != NULL))
		{
            if(Hight(Root->Left) > Hight(Root->Right))
			{
				Root->element = GetMaxNode(Root->Left)->element;
				Delete(Root->element, &Root->Left);
			}
			else
			{
				Root->element = GetMinNode(Root->Right)->element;
				Delete(Root->element, &Root->Right);
			}
			
            Root->Hight = max(Hight(Root->Left), Hight(Root->Right)) + 1;
		}
		else
		{
			if((Root->Left == NULL)&&((Root->Right == NULL)))
			{
				pRoot[0] = NULL;
			}
			else
			{
				pRoot[0] = (Root->Left != NULL)?Root->Left:Root->Right;
                pRoot[0]->Hight = max(Hight(pRoot[0]->Left), Hight(pRoot[0]->Right)) + 1;
			}
			
			free(Root);
		}
	}
	
	return TRUE;
}

到这里,平衡二叉树的大体 操作流程已经讲完了,希望对您有所帮助。我会在评论附上对应的QT工程源码,可以通过图形的方式展示一下效果

https://download.csdn.net/download/a18363939205/21928394

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值