学习数据结构的第十天(二)(包括一些还没有学习的东西的指引)

还没有学习的东西的指引:对于这个bst的中序、后序的非递归??

至于为什么这个avl树旋转是logN  这个其实我也不太知道。

这里再看:avl树的底层存储结构并不是说:并不是bst树,而是说同样地:存储根节点和size,原因:它的remove和add操作和普通bst不太一样的。

花了好久,终于把avl树的写法给弄好了,学会的技术细节:

扫描二维码关注公众号,回复: 10562563 查看本文章

思路历程需要学习的:

1.Math这个库是自带的,直接用Math就可以了,不用带什么库import

2.对于Node而言,需要添加height这个字段,这个是用来看高度到底为多少的,越往上,根越高。

3.height表示的是层次和高度,越往上,高度为高。  引入这个节点的目的是:为了去检测左右子树的深度差是否大于1,即是否平衡这一条件。如果大于1的话就认定是不平衡了。

 4.

 这是判定 是否为二叉搜索树的一个函数,就是中序遍历之后,查看是否从小到大排序。

如果非从小到大排序,那么就不是二叉搜索树。

5.对于树或者说递归之类的结构,必须要搞清楚 函数的定义是什么, 你需要拿这个函数返回的值是什么?

返回的是:这个根的高度?还是说这个以这个为根的子树的高度。

6.对于特殊情况来说,自己区分一下,有的时候和  出口是同样的意思,有的时候和出口不同,要和出口相区分。

public int getHeight(Node node) //height的定义,返回以node为根的树的高度
//key:必须清楚返回的是当前节点,还是说返回的是以node为根的树的高度
{
if(node==null) //其实在这里也让我看到,除了说:作为特殊情况,其实特殊情况和出口是一体的感觉
return 0; //空树没高度
else
{
return Math.max(getHeight(node.left),getHeight(node.right))+1;
}
}

7. 不管是哪些容器,底层都写好了一些add remove之类的。

8.平衡因子的概念:   左子树的高度减去右子树的高度。。   key:说的是左子树的高度减去右子树的

左边去减去右边,是为了告诉你:左右子树是否相差1以及以上。

9.

 balanced这件事:找准这个函数的定位就比较能知道怎么去写了。

函数的定位:以node为根的树是否平衡

就是说:  如果说是空树的话,那么是平衡的。   如果说当前是平衡的(平衡因子的概念就用到这里了),那么还要去看子树是不是平衡

如果说当前不是平衡的,那么就直接返回是错。

10.

 右旋的含义

首先搞清楚:左左偏的含义(LL)

   

 首先,搞清楚LL的含义,RR的含义,说的是前面的两根线。到达末尾的时候,添加两个子树

对于另外的两个而言,也认定补全其结构。像图2的F和G就可以补

对于左旋、右旋的含义是:对于根节点而言,左旋右旋。

其实是一个和LL  RR对应的概念来的。(左旋的意思是逆时针,右旋的意思是顺时针)

 意思是说:

记住:T1 T2在这个位置,一定是1级的,虽然说变矮了。但是高度是由它的子树的多少决定的,由子树的高度决定的,而不是由处于的位置决定的。

但是的话x和y可能下降:   原因:如果说t1是空 t2是空的话  那么y下降了。

如果说T3 T4  是要求不为空的,  那么x其实会随着y下降而下降吗?

其实不太会。

也就是说:旋转了之后,记得要调整高度,调整高度是很重要的。

旋转其实就是指针的改变,x和y的子树的指向的改变。

11.

 记住,对于这里的话,只调用,不做其他的事情。

原因:对于这些height的改变,需要当场add完之后就改变。所以在它内在的调用函数实现height的改变

而不是说在外面调用的接口这做一些事情。

12.

 在添加逻辑里面,其实这个为空的,既是特殊情况,也是出口条件

对它的子树添加完了之后对于这个节点,需要改变其高度

也就是说改变高度这件事情是很重要。

13.对于旋转的选择时机逻辑:

key:意思是说:    对于大于1,或者说小于-1,那么说明不平衡 到一个地步了。

那么如果说是left小于等于0(记得等于也是ok的),这是等于0的情况

 

只需要知道根的不平衡情况达到了条件,并且知道相应的 方向的  不平衡情况   是  同一方向(等于0也算是同一方向)即可。

 这里说的是:    需要知道  根的子树的方向是逆方向,必须是 逆的 所以必须是小于0

 

你细品即可。

对于什么时候应该选择旋转都知道了。

 底部先旋转,得到答案,再往上。

双旋转,那么需要先对底层进行旋转,再对上层旋转。

 

 先保存returnNode  然后的话size--肯定是在这里写。

外面的remove接口不写size--  原因:  remove接口就只调用这件事,不做其他的事情。

size--的话,因为就算是左右都不为空的情况下,那么也是找最小的节点,它的子树为空的。

 因为在外面的话,它的returnNode需要看,如果returnNode为空了,直接return 

然后还有改变高度这件事情,也是在remove之后的外面逻辑去改变高度。

然后的话外面就是旋转的逻辑在了。

也就是说:对于节点新添加的字段,各种操作会怎么影响它这个新的字段,需要考虑

对于递归的增加,递归的删除。在相应部分对其子节点处理完之后,对该节点也需要进行高度的改变等操作。

改变的是子节点   还是该节点 需要注意,递归的返回,需要对该节点的高度等也进行处理。

处理完子之后处理当前

也就是说对于递归的结果的返回,如何改变高度这件事情,递归性返回改变的高度这个结果。     就是通过:  解决完子节点之后解决当前结点   来处理的。

并且   :       引入returnNode  先保存当前结果这件事是值得学习的。

avl树已经写完了,并且里面我写错的逻辑也进行了订正。

 对于avl树去实现map,实现set都不用说,其实就是调用其中的函数而已。拿一个avl去作为底层的存储结构。

这里set和map其实就是说添加的逻辑不一样而已。只添加key,就是set   添加key和value就是map

avl树引入的目的:是为了退化成线性表的不被发生,防止的是最坏的情况都有logN

猜你喜欢

转载自www.cnblogs.com/startFrom0/p/12650358.html