AVL树的实现:
1. AVL树的具体实现前需要研究的内容
AVL树就是在BST也就是平衡二叉树的基础上进行条件的约束被,就是左右子树的高度差不能超过1,如何保证,高人也帮你想到了,直接左旋,右旋,,右左旋或者右左旋转被,直接平衡。高人连在什么时候用都帮你准备好了,左子树失衡,就最后左旋,右子树失衡就最后右旋,左子树失衡又分为左的左引起的失衡还是左的右引起的失衡,这部分有个技巧只有当左的左为空时才是左的右引起的失衡,左的左在时永远时左的左引起的失衡,与左的右没有关系,为什么,我们都是以左的左为核心旋转的,左的右就是先右旋转化为左的左,再左旋的,你左的左本身就存在,不需要我们人为制作,这个具体为啥,谁知道呢,我们不是高人,也不明白他具体咋来的,我们只知道,用这个就能平衡,左的左左旋后就能平衡,旋转就是个工具,因此我们将高人研究的技巧做成工具:具体如何做的看我代码或者寻找其他人博客都有介绍。只要看过一次,下次直接自己画个图直接就能写出了,因为就是个工具吗,没有啥变化,
就例如左的左引起的失衡,直接左变成新的根,根变为左的右边,左的右边变成根的左即可,这部分建议画个图,要不容易顺序搞错,首先记得先拆后连,先拆下左保存在一个变量里,表示根的左已经被断开,根的左是空的,这时将左的右连接到根的左上,二叉树没有父亲节点,直接根的左等于左的右即可,左的右等于根即可,三个赋值就解决了,还有一个情况需要考虑,也就是根的父亲问题,这个有两种方式去解决,一种在函数内部直接解决父亲连接问题,一种是借助于递归在函数外进行连接
a. 假设我们有一个父亲节点(可以结构体外申请一个节点一直保存当前节点即引起失衡节点的父亲,或者二叉平衡搜索树直接包含父亲),直接加一个判断,失衡的点也就是现在树的根节点(总树以一部分),判断他是在父亲的左还是右直接让他的父亲等于左就ok了,这部分还有一个坑要记得,就是根,现在的根就是总树的根,没有父亲,没有父亲,你判断父亲的左或者右,NULL调用结构体里内容会报错的,因此先判断父亲是否是NULL,NULL则直接左成为总根,(所以你穿进来的必须是二级指针的根,一级指针不行吧,(为啥不行,pRoot = new,直接断开原来联系,pRoot形参变化不会影响实参,我们要修改pRoot,因此需要*pRoot = new,因此需要传的事pRoot的地址,而不是pRoot指向的地址。)不是NULL,则直接换就完事了被,父亲的左或者右是根,然后将根换为左结束。
b. 返回根节点,即每次改变后直接,即直接按照上述旋转过程返回左即可,具体要怎么连接我这个函数不管,我直接把我这部分完事后返回你个根就完事。
2. 其实上述部分都是废话,BST树的左旋右旋,左右旋,右左旋,谁不知道,就那点代码,BST的添加,删除也就那样,添加就是满足要求下找到空缺的位置,连上就行,删除就是找左的最右或者右的最左替换一下,变成只有一个孩子或者没有孩子的情况,直接让爷爷连接孩子,删掉父亲,完事被,唯一麻烦也就是父亲怎们连接孩子,和上边一样,采用递归连接,或者设计变量一直保存父亲。这东西谁不知道呢,我最想知道是AVL树的高度怎么搞得,这么乱七八糟的树为什么他知道什么时候调用哪个旋转,要是人眼的一眼就看出了,但是电脑不知道啊。因此我具体分析AVL树高度问题。怎么分析,一步一步分析被。
添加:BST加判断加平衡(各种旋)变成AVL
a. 设叶子节点的高度为0,NULL的高度为-1(这部分很关键,得自己定义你对于树的高度的理解,我认为树根最高所以,设置叶子节点为0,为什么NULL高度为-1,为了方便啊,为了方便计算AVL树的平衡因子,再说,叶子节点为0,NULL作为叶子节点的孩子肯定是-1,合情合理,其实还是为了计算方便)
b. 高度怎么计算得到呢,就是左右孩子大的那个+1.
c. 添加:BST添加,(不会的建议先研究BST)。
添加完后BST直接结束,但是AVL还需要保持左右子树高度不超过1,其实添加过程BST和AVL树大部分是一致的,为了方便理解我们假设现在有个AVL树,我们用BST方法插入,使得AVL就退化为BST树了,也就是我们导入了导致不平衡的点,我们怎么搞才能恢复AVL,画个图一看其实就知道了,我们首先要干什么,当前重新分配高度啊,插入新的点了,因此我们从新的点开始往上找,依次更新高度,直到找到左右孩子不平衡的点,旋转使其平衡。一直从新添加节点找到根位置,因为只要添加了节点,添加节点的树上都可能导致了不平横。现在我们先不要管怎么解决不平横,也不需要知到如何找到不平衡,我们现在要知道的是哪里可能不平衡即可,那么分析出,假设我们有两个函数一个可以判断是否不平衡,一个可以解决不平衡,或者直接聚合成一个函数直接判断是否是平衡的并解决不平衡。那么我们如何知道哪里可能不平衡呢,我们发现我们添加的时候,遍历到所有节点都包含新节点,因此都是可能不平衡的点,怎么保存这些点呢,我们发现这就是一个递归的过程吗,一直递归到底部,然后回溯到根,因此我们可以采用递归保存这些点,那还可以怎么保存呢,怎么保存先进后出很明显把,因此利用栈保存所有经历的节点也可以,然后将栈分别弹出分别重新计算高度,利用步骤B得到函数得到新的高度即可,进入函数测试是否平衡和以及解决不平衡。
d. 上一步骤中我们发现需要一个判断是否是不平衡的函数,那么怎么能判断是否平衡呢,很简单了,定义啊,左右孩子高度差不能超过1啊,因此我们设计一个函数得到左孩子高度,一个函数得到右孩子高度,为什么要设计函数,减少麻烦的判断吗,左可能不存在,右也可能不存在啊,不能每次都判断把,因为不存在要返回-1,因此设计一个函数。设计只有就更简单了,直接让左的高度减去右边的高度,
i. 大于1,则是由左引起的不平衡
1) 判断左的左存不存在,这不直接套模板了吗,就是我那堆废话里说的
a) 在直接右旋
b) 不在先左旋后右旋
ii. 小于-1,这是由右边因此的不平衡
1) 判断右的右存不存在
a) 存在直接左旋
b) 不存在先右旋再左旋
iii. 否则,平衡被,不需要搞啥
e. 设计左旋右旋,左右旋右左旋被
添加结束了,是不是简单很,把所有情况设计成函数,我们要把复杂的问题简单化
删除:有点区别于BST,实际差不多
a. 高度问题和左右子树平衡问题上:和AVL添加部分一致,都是递归重新划分节点的高度,对每个包含删除节点的树进行平衡重新划分节点的高度,重新判断是否平衡以及解决不平衡,这部分和添加的一样
删除结构上和BST一样:都是判断在左还是在右递归寻找,找到后判断几个孩子,1个或者0个孩子,直接删除父亲返回孩子即可,要是两个孩子,把左的最右或者右的最左的值换一下,从左或者右开始找要删除的那个点即可,(传入左或者右取决于你选择哪个替换的),但是有一个区别:BST随你喜欢左的最右或者右的最左都可以,但是AVL必须是左子树或者右子树的高度比较高的点的最后那个点进行替换。这样搞因为什么,简单啊为了调整的节点树最小被,因此为了方便我们再设计两个函数,一个直接得到根的左的最右,和一个直接得到根的右的最左的函数,判断一下左子树高度-右子树高度是否大于0即可大于则左侧高,调用左的最右替换即可。
代码#include<stdio.h>#include<stdlib.h>typedef struct tree{ int nValue; int nHeight; struct tree *pLeft; struct tree *pRight;}AVL;int GetHeightMAX(AVL *pTree);int GetHeightLeft(AVL *pTree);int GetHeightRight(AVL *pTree);AVL *Creat