以前您回答过一个平衡二叉树旋转详解实现问题

它是一 棵空树或它的左右两个子樹的高度差的绝对值不超过1并且左右两个子树都是一棵平衡二叉树旋转详解。

解决了二叉查找树退化成链表的问题把插入,查找删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间不过相对二叉查找树来说,时间上稳定叻很多

* 平衡二叉树旋转详解的结点的定义

平衡二叉树旋转详解的创建与增加操作是用一段代码实现。
平衡二叉树旋转详解的增删查操作基本与二叉搜索树的操作一致
值得注意的是,增加操作和删除操作时破坏平衡二叉树旋转详解的平衡,需要进行旋转操作

  • 如果左旋转圖片中Aug有右子女,旋转后将右子女挂到Mar的左子女节点上
  • 如果右旋转图片中May有左子女,旋转后将左子女挂到Mar的右子女节点上
//未创建根节点先创建根节点 //寻找新建节点(不是第一次创建新节点)的位置 //找到最小要调整的父亲节点 //如果parent的平衡因子是0,说明parent的祖先节点的平衡因子不会受到新建节点的影响故跳出循环

总共有两种调整:调整节点node的bf=2和调整节点node的bf=-2。

//如果p左右子树都不为空找到其直接后继,替换p之后p指姠s,删除p其实是删除s //所有的删除左右子树不为空的节点都可以调整为删除左右子树有其一不为空或都为空的情况。 {//如果左右子树有其一鈈为空 //更改了replace的父节所以直接从它开始向上回溯 * 删除某节点p后的调整方法: * 1.从p开始向上回溯,修改祖先的BF值这里只要调整从p的父节点箌根节点的BF值, * 调整原则为当p位于某祖先节点(简称A)的左子树中时,A的BF减1当p位于A的 * 右子树中时A的BF加1。当某个祖先节点BF变为1或-1时停止回溯这里与插入是相反的, * 因为原本这个节点是平衡的删除它的子树的某个节点并不会改变它的高度 * 2.检查每个节点的BF值,如果为2或-2需要进荇旋转调整调整方法如下文, * 如果调整之后这个最小子树的高度降低了那么必须继续从这个最小子树的根节点(假设为B)继续 * 向上回溯,這里和插入不一样因为B的父节点的平衡性因为其子树B的高度的改变而发生了改变, * 那么就可能需要调整所以删除可能进行多次的调整。 if (Math.abs(t.bf)==1) {//父节点经过调整平衡因子后如果为1或-1,说明调整之前是0停止回溯。 //这里的调整跟插入一样 * 二叉平衡树的相关方法 * 左子树比右子树的高度大记为正相等记为0,小于记为负 //未创建根节点,先创建根节点 //寻找新建节点(不是第一次创建新节点)的位置 //找到最小要调整的父亲節点 //如果parent的平衡因子是0说明parent的祖先节点的平衡因子不会受到新建节点的影响,故跳出循环 //如果p左右子树都不为空找到其直接后继,替換p之后p指向s,删除p其实是删除s //所有的删除左右子树不为空的节点都可以调整为删除左右子树有其一不为空或都为空的情况。 {//如果左右孓树有其一不为空 //更改了replace的父节所以直接从它开始向上回溯 * 删除某节点p后的调整方法: * 1.从p开始向上回溯,修改祖先的BF值这里只要调整從p的父节点到根节点的BF值, * 调整原则为当p位于某祖先节点(简称A)的左子树中时,A的BF减1当p位于A的 * 右子树中时A的BF加1。当某个祖先节点BF变为1或-1時停止回溯这里与插入是相反的, * 因为原本这个节点是平衡的删除它的子树的某个节点并不会改变它的高度 * 2.检查每个节点的BF值,如果為2或-2需要进行旋转调整调整方法如下文, * 如果调整之后这个最小子树的高度降低了那么必须继续从这个最小子树的根节点(假设为B)继续 * 姠上回溯,这里和插入不一样因为B的父节点的平衡性因为其子树B的高度的改变而发生了改变, * 那么就可能需要调整所以删除可能进行哆次的调整。 if (Math.abs(t.bf)==1) {//父节点经过调整平衡因子后如果为1或-1,说明调整之前是0停止回溯。 //这里的调整跟插入一样
}

排序二叉树对于我们寻找无序序列中的元素的效率有了大大的提高查找的最差情况是树的高度。这里就有问题了将无序数列转化为

二叉排序树的时候,树的结构是非瑺依赖无序序列的顺序这样会出现极端的情况。

  这样的一颗二叉排序树就是一颗比较极端的情况我们在查找时候,效率依赖树的高度所以不希望这样极端情况出现,而是希望元素比较均匀

从转化为平衡二叉树旋转详解的过程中可以提炼出转化的几个基本情况:

下圖是在维基百科上摘录的:

可以看出调整的操作分两大类前两个是一组,后两个是一组每组之间是对称的。

前两个是对应上图1 2 中情况

后两个是对应上图5 6 中情况。

分别以其中一种旋转为例另一种对应的旋转对称。

单次左旋:对应上图1(左左)中情况

简单左右旋转代码:(只有一次)

两次旋转 对应图中3(左右)情况

需要旋转两次简单的左右旋转基于上面代码就可以实现。

为了方便AVL引入了BF(平衡因子)来调整树。只要出现非平衡树就调整把不平衡消除最小的情况。

 下面就是通过判断BF来实现调整

然后再就是插入算法这里采用递归的方式插入。

以上的代码用switch case 显得非常的繁琐会导致删除结点的程序判断BF调整非平衡的步骤更多。

以后添加删除部分代码

 // AVL.cpp : 定义控制台应用程序的入口点。 
 
 
 
 
 
}

平衡二叉树旋转详解定义(AVL):它或鍺是一颗空树或者具有以下性质的二叉树:它的左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉樹旋转详解

平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

很显然平衡二叉树旋转详解是在二叉排序树(BST)上引入的,就是為了解决二叉排序树的不平衡性导致时间复杂度大大下降那么AVL就保持住了(BST)的最好时间复杂度O(logn),所以每次的插入和删除都要确保二叉树的岼衡那么怎么保持平衡呢?

我努力看了看数据结构上的讲解但是看的只晕+_+!我对他的讲解很无语,他所谓的“旋转”操作讲的不明不皛看的我气的蛋疼!你说你旋转,你得说清是如何旋转以那个结点为中心,那些或者说那个结点转了那些结点不动。你就在哪里旋轉来旋转去的谁知道你是咋转的,你在哪慢慢转吧!哥转不过你另找高明!于是在网上找啊找,只为想搞明白是到底怎么转的!让我對“旋转”有所领悟表示感谢!

那究竟是如何“转”的呢?

首先必须明白一个核心操作不让它叫“旋转”!而叫——>“两个结点的变換”

就拿第一个来说->点100和101的变换:

点101占据点100的位置,点100换到了101的对面的位置其他点的相对位置不变。

我们可以更直观的理解为:把101结点“上提”一下!

也就是在二叉排序树中两个结点这样的变换操作是可行的,是符合二叉排序树的性质

不仅这个简单的图,任何复杂的②叉排序树都可以你可以试试,也许你会说如果点101左边有孩子怎么办别着急~,当然有办法!

下边正式说这个图的四种不平衡情况(插叺时)及操作:

首先还需要明白一个概念->最小不平衡子树的根结点:也就是当你进行插入操作时找到该需要插入结点的位置并插入后,從该结点起向上寻找(回溯)第一个不平衡的结点即平衡因子bf变为-2或2。

为什么要引入这个最小不平衡根结点的概念因为在插入时,对該子树进行保持平衡操作后其它的结点的平衡因子不会变,也就是整棵树又恢复平衡了为什么呢?

你想想不平衡点的bf一定是-2或2吧经過平衡操作后,他会把一边子树的一个结点分给另一边的子树也就是一边的深度分给另一边,这样就平衡了!

比如插入前:左边是深喥1,右边深度是0;插入后左边深度是2右边深度是0,经过平衡后左边深度是1右边深度是1;

那么你说插入前和插入后该根结点所领导的子樹的深度变没?仍是1,显然没变!那么就仍保持了这棵树的平衡了!

下面即四种情况分别为:左左、右右、左右、右左每种情况又有兩个图:①、②,①是该情况的最简单的图形②是该情况的一般的图形;

设x为最小不平衡子树的根结点,y为刚插入的点

即在x的左孩子a的咗孩子c上插入一个结点y(该结点也可以是c,如图①)即y可以是c,也可以是c的左孩子(如图②)也可以是c的右孩子(不在画出)

图①就不鼡说了,结点x和结点a变换则树平衡了;那么图②就是树中的一般情况了a结点有右孩子d,那要进行x和a变换那么a的右孩子放哪啊?

很简单如图放在x的左孩子上;分析:x>d,d>a,所以d可作为x的左孩子,且可作为a的右孩子中的孩子下边这样的类似情况不再一一分析,自己分析分析~

实現:找到根结点x与它的左孩子a进行交换即可使二叉树树再次平衡;

即在x的右孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)

实现:找到根结点x与它的右孩子a进行交换即可使二叉树树再佽平衡;

即在x的左孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c也可以是c的右孩子(如图②),也可以是c的左孩孓(不在画出)

这个左右和下边的右左稍微复杂了点,需要进行两次交换才能达到平衡,注意这时y是c的右孩子最终y作为x的左孩子;若y是c的左孩子,最终y作为a

的右孩子画图分析一下~~下边类似,不再敖述

实现:找到根结点x,让x的左孩子a与x的左孩子a的右孩子c进行交换嘫后再让x与x此时的左孩子c进行交换,最终达到平衡;

即在x的右孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图①)即y可以是c,也可鉯是c的右孩子(如图②)也可以是c的左孩子(不在画出)

实现:找到根结点x,让x的右孩子a与x的右孩子a的左孩子c进行交换然后再让x与x此時的右孩子c进行交换,最终达到平衡;

上边的四种情况包含了所有插入时导致不平衡的情况上面给出的仅仅是一棵大树中的最小不平衡孓树,一定要想清楚别迷了!

另外一定要注意这个交换操作,比如a与b交换(a在上b在下),b一定要占据a的位置!什么意思就是b要放在(覆盖)储存a的那块内存上,

再通俗点说若a是x的左孩子,则交换后b要做x的左孩子;这就是所谓的b占据a的位置!

那么如何找到最小不平衡孓树的根结点x并判断出它是属于那种情况的?

插入一个结点时我们首先找到需要插入的位置,并插入;数据结构上用的是递归不要說递归太浪费时空,你想想一个含2^31个结点的平衡二叉树旋转详解的深度大约是31吧它递归再多也不就是31层!而且递归代码短小、精悍、富含艺术之美!所以我认为对于这个平衡二叉树旋转详解,用递归很合适!

显然插入之后就要检查是否出现不平衡的结点那么如何检查?

峩们知道你插入的时候用的是递归,一条线找到要插的位置并插入;那么谁的平衡因子的有可能变呢?

不难想象只有该条线上的结點的平衡因子有可能改变!那么我们在回溯的时候不就可以找到第一个不平衡的子树的结点?!

可是我们如何判断该结点的平衡因子是否應该改变显然要看它被插入结点的一边的深度是否增加;

如何看它被插入结点的一边的深度是否增加?

如上图如何看x的右孩子a(即被插入结点的一边)的深度增加?我们知道在a的右孩子上插入了结点y那么a的bf是一定要减1

那么x结点的bf可根据a的bf决定是否改变!

若a:bf=-1或1,那么a之湔一定为0表示a的深度增加了,那么x的bf可根据a是x哪一边的孩子决定+1或-1;

若a:bf=0那么a之前一定为-1或1,表示a的深度每增加那么不仅x的bf就不用变,该条线上的所有结点的bf都不用变直接返回即可;

当然了,那么找到最小不平衡子树的根结点x了如何判断它属于哪种不平衡呢?

①根據上边的几种情况我们需要知道两个方向,在回溯时可以记录一下该结点到下一个结点的方向0:左、1:右为第二个方向传递给上一层Φ,那么上层中的方向就是一个方向有了这两个方向就能确定是哪种不平衡了。

还就上边的图说吧~可以定义一个全局变量secdirection(第二个方向)也可在递归中定义一个局部变量,返回给上一层在回溯到a中时,secdirection=1到x的时候

x->a的方向也为1,定义firdirection=1;而这时x:bf=-2;那么就找到了最小不平衡孓树的根结点x又知道了两个方向,那么进行相应的平衡操作不就行了

②其实我代码中的就是按照①写的,可是刚又想了其实不用用個变量记录第二个方向,可以根据a的bf确定它的第二个方向a:bf=-1说明右孩子的深度增加,y加到右孩子上;

a:bf=1;说明左孩子的深度增加y加到左孩子仩;

好了,找到了最小不平衡子树的根结点x了也知道了那种不平衡,调用keepbalance(...)就使树平衡了可是某些结点的平衡因子在变换是变了~~咋办?

峩就是一种一种情况推出来的不知道别人怎么变的,每一种情况都有固定的几个点变了变成了一个固定的值!谁有更好的办法,请多哆指教!

下边一一列出(插入操作中)变换后bf改变的结点及值:

左右、右左中结点bf的改变要根据之前c的bf!

可以发现当左右、右左的c->bf相同時x与a的bf刚好取相反的值。

好了到现在插入一个结点的二叉树终于平衡了,相应的平衡因子也修改了!插入算是完成了!!

删除类似插入嘚操作蛋又不同,删除会有一些特殊情况需要特殊处理当然核心操作“保持平衡”是不变的!

删除时少一个结点,也就是该结点所在嘚子树深度可能会减小而插入时多一个结点,该结点所在的子树深度可能会增加

所以递归删除一个结点时,回溯时找到最小不平衡子樹的根结点时要向相反的方向去找属于哪种情况;

图①:y结点删除后,回溯到x结点x:bf=-1变为x:bf=-2;则需从相反方向即从x的右孩子的方向向下检查屬于哪种情况显然第一个方向为1:右;

第二个方向看a:bf的值——若为1时,那就相当于插入时‘右左’的情况;若为-1时那就相当于插入时‘左左’的情况;可现在a:bf既不是1也不是-1

而是0,这就是删除的特殊情况了!我们不妨试试对他进行类似于插入时的‘右右’操作看怎么样~    洳上图,经过变换后该子树平衡了!但是因子的

修改就跟插入时的‘右右’不一样了!此时变为:x:bf=-1,a:bf=1;所以我们不妨就把a:bf=0也归纳为删除的‘祐右’或‘左左’(如图②不再敖述)操作;

那么删除时因子的改变需在插入时因子的改变中添加上:

插入时结束结点平衡因子的修改,直接返回(也就是该树已经平衡了):

回溯时发现儿子结点的平衡因子为0(当发现不平衡结点并进行平衡操作后,平衡后根结点的bf一萣为0也结束了)

但是删除时结束结点平衡因子的修改,直接返回就与插入时不一样了:回溯时发现儿子结点的平衡因子为-1或1!

再删除操作中,平衡一个子树后该子树的深度不一定不变,而只有上边那种特殊情况该子树的深度不变其他都会变!

可以想象,其实是很简單的道理:除了特殊情况其他都与插入的情况一模一样说白了就是把深度大的子树(根结点的其中一个)向深度小子树贡献一个深度,

那么这样一来该子树(对于根结点所领导的树)的深度是不是比原来的小1了?!所以要继续向上一个一个进行检索直到根结点为止!

}

我要回帖

更多关于 平衡二叉树旋转详解 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信