20172328 2018-2019《Java软件结构与数据结构》第七周学习总结
概述 Generalization
本周学习了第11章:二叉查找树。在本章中,主要探讨了二叉查找树的概念和各种二叉查找树实现,考察为二叉查找树添加和删除元素的算法以及维护平衡二叉查找树的算法
教材学习内容总结 A summary of textbook
- 二叉查找树(binary search tree):二叉树定义的扩展,一种带有附加属性的二叉树。附加属性是什么?树中的每个节点,其左孩子都要小于其父节点,而父节点又小于或等于其右孩子。
-
插入元素(addElement):
(1)不允许插入相同关键字,若二叉查找树中存在该关键字,则不插入
(2)我们可以先检索二叉树,看查找树中是否含有该关键字,若不存在,再做一次扫描将结点插入到适当位置。使用这种方式,为插入一个该关键字,做了两次扫描。
(3)注意到,插入的结点总是作为某一叶子节点的子结点,我们可以在第一次扫描过程中就确定待插入的位置,即把查找是否存在该关键字和查找可能的插入点在一次扫描中完成,提高插入效率。
-
删除元素(removeElement):
在二叉查找树中删除一个给定的结点p有三种情况:(1)结点p无左右子树,则直接删除该结点
(2)结点p有左子树(右子树),则把p的左子树(右子树)接到p的父节点上
(3)左右子树同时存在,找到结点p的中序直接后继结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的右子树总最左的结点,因而s无左子树,删除结点s.
-
平衡二叉查找树:
- 引入:为什么平衡假设很重要?如果树不平衡,我们的分析会出现什么情况?
- 如果我们的树不平衡,那么得到的二叉树其实是一棵蜕化树,看起来像链表,并且比链表的效率还低,因为每个结点都附带多于的开销。addElement操作的复杂性为O(n)。而且蜕化树要浪费空间来存放那些未用的引用,并且很多算法会在沿着蜕化路径前进之前检查null引用。
- 那么该如何解决这个问题呢?
- 我们的目标是保持树的最大路径长度为log2(n)。解决的办法就是要有一个称为平衡的附加结构条件即:任何结点的深度不得过深。
- 平衡二叉树(Balanced Binary Tree)的定义:一棵平衡二叉树是其每个结点的左子树和右子树的高度最多相差1的二叉查找树(空树的高度为-1),这个差值也称为平衡因子(本书定义的平衡因子:右子树的高度减去左子树的高度),当平衡因子大于1或者小于-1的时候以该结点为树根的子树需要重新平衡。
- AVL树只是实现平衡二叉树的一种方法,它还有很多的其他实现方法如红黑树、替罪羊树、Treap、伸展树等。
-
平衡化技术通用方法:
- 右旋:
- 使树根的左孩子元素成为新的根元素。
- 使原根元素成为这个新树根的右孩子元素
- 使原树根的左孩子的右孩子,成为原树根的新的左孩子。
- 左旋:
- 使树根的右孩子元素成为新的根元素。
- 使原根元素成为这个新树根的左孩子元素
- 使原树根的右孩子的左孩子,成为原树根的新的右孩子。
- 右左旋:
- 让树根右孩子的左孩子绕着树根的右孩子进行一次右旋。
- 然后再让所得的树根右孩子绕着树根进行一次左旋。
- 左右旋:
- 让树根左孩子的右孩子绕着树根的左孩子进行一次左旋。
- 然后再让所得的树根左孩子绕着树根进行一次右旋。
- 右旋:
- 运用这些平衡手段的条件:
- 旋转发生的条件:
左右子树的高度差为二,旋转有两种::单旋转,双旋转。无论单旋转还是双旋转,都满足先进行与深度较高的子树同名(右子树对右旋转)旋转,如果是双旋转则随后进行异名(右子树对左旋转)旋转
- 单旋转发生在:
- ①插入时:
- 插入的节点在不平衡点的左子树的左子树上(左单旋);在不平衡点的右子树的右子树上(右单旋)
- ②删除时:
- 删除左子树的节点导致该点不平衡且其右子树的右子树存在时(右单旋);删除右子树的节点导致该点不平衡且其左子树的左子树存在时(左单旋)
- 双旋转发生在:
- ①插入时:
- 插入的节点在不平衡点的左子树的右子树上(左右旋);在不平衡点的右子树的左子树上(右左旋)
- ②删除时:
- 删除左子树的节点导致该点不平衡且其右子树的右子树不存在时(右左单旋);删除右子树的节点导致该点不平衡且其左子树的左子树不存在时(左右单旋)
实现二叉查找树:红黑树
- 红黑树是一个更高效的检索二叉树,因此常常用来实现关联数组。典型地,JDK 提供的集合类 TreeMap 本身就是一个红黑树的实现。
- 红黑树控制结点颜色的规则如下:
- 性质 1:每个节点要么是红色,要么是黑色。
- 性质 2:根节点永远是黑色的。
- 性质 3:所有的叶节点都是空节点(即 null),并且是黑色的。(性质 3 中指定红黑树的每个叶子节点都是空节点,而且叶子节点都是黑色 Java 实现的红黑树将使用 null 来代表,因此遍历红黑树时将看不到黑色的叶子节点,反而看到每个叶子节点都是红色的。)
- 性质 4:每个红色节点的孩子结点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
- 性质 5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。(红黑树从根节点到每个叶子节点的路径都包含相同数量的黑色节点,因此从根节点到叶子节点的路径中包含的黑色节点数被称为树的“黑色高度”。)
- 红黑树能够以O(log2(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。这一点是AVL所不具备的。
教材学习中的问题和解决过程 Problem and countermeasure
-
问题1:不明白书中removeElement方法中部分代码的意思。书中解释是:
如果被删除结点有两个孩子,则返回中序后继者(因为相等元素会放到右边)
代码是
- 问题1的解决及过程:
- 当结点的左右子树都不空的时候,一般的删除策略是用其右子树的最小数据来代替要删除的结点数据,并使得要删除子树的双亲结点指向找好的替代结点的子结点。
- 老师在课堂上讲了另一种解决办法: 当结点的左右子树都不空的时候,找到该结点的前驱结点,然后将其前驱结点(用其左子树的最大数据)作为替代结点,然后让要删除子树的双亲结点指向替代结点的子结点。
- 前驱结点:该结点左子树中的最大数据所在结点。
- 后驱结点:该结点右子树中的最小数据所在结点。
- 总体来讲:一棵二叉查找树经过中序遍历后是一个有序的、从小到大的列表。
- 问题2:高度和深度究竟有什么区别?之前在学第十章:树的时候曾和仇夏同学讨论过,但是没有得到明确的结果。当时老师上课讲的是深度的概念,而且从root开始以1来计算、而书本上讲的是树的高度的概念,树的高度是从跟结点到叶子结点之间的的最远路径长度,并且从root开始以0来计算。???
- 问题2的解决:
- 高度和深度一组相反的概念
- 高度是指当前结点到叶子结点的最长路径,如所有叶子结点的高度都为0。
- 深度则是指从根结点到当前结点的最大路径长度,如根结点的深度为0。
- 所以,书上的讲解和老师的讲解其实都没有问题,只是思维理解上的问题。高度就是从要计算的结点开始寻找最远路径长度,深度就是从根结点到该结点的最大路径长度;在一棵树中,高度+深度 = 树中最远的路径长度。
- 问题3:红黑树的addElement操作和removeElement操作看书时没有看太懂,好多疑惑。
- 问题3的解决:通过周五早上老师上课的讲解,觉得还是听懂了一些的。这里稍加总结并配上老师的图。
代码实现时的问题作答 Exercise
- 问题1:这周的代码补全很容易理解,在我做完补全操作后,我又和我的小伙伴仇夏同学订对了一波,于是我们发现了之前写的findMin以及findMax由于使用了之前写的removeMin和removeMax的思路,在写的时候其实有冗余代码。
问题1的解决:删掉冗余代码。
问题2: 实现add自我递归时出现的错误。首先是死递归,其次是出现了一行不明错误提醒。
-
问题2的解决:先是来解决不明错误提醒,我通过百度后得知:
原来Junit4中的测试方法的方法名首字母不能大写!!!
通过修改代码后,我发现是因为我的node直接定义成了root,所以在每次递归的时候又开始之前的路,就变成了死递归,在删除代码·
node=root
后测试就出来了。但是由于toString问题没有解决,我用了中序遍历,但是还是出现了很令人伤心的结果,但通过找最大最小值,看的出来,我的插入方法已经成功将1插入了二叉查找树。- 问题3:上述问题2没有实现树型输出,主要问题就在我的toString方法中,但是我没有实现树形状的toString方法。
问题3的解决:通过询问郭恺同学,借用了上周运算表达树中PrintTree的输出方法,稍作修改,就很完美的实现了树形的正确输出啦。
上周测试活动错题改正 Correction
- The binary tree shown above is balanced.
A .True
B .False -
这道题我真的没有找到图,所以不知道她平不平衡。所以没啥好说的······给出的答案是true。
码云链接
代码量(截图)
结对及互评Group Estimate
点评模板:
- 博客中值得学习的或问题:
- 20172301:博客反复的在改动,尤其是代码问题上反复在思考在修改描述,认真的对待博客正如认真的对待代码。
-
20172304: 对于学习知识点掌握应该是挺好的,上课时对问题反应很迅速。本次博客教材学习中的问题和解决过程和代码问题都写的不是很详细,望继续改进。
其他(感悟、思考等,可选)Else
今天早上的经济学原理课程听得我很快乐。范老师十分幽默风趣,讲到了:在我们面临稀缺而选择实现效益最大化时就要体现自身价值,在“万类霜天竞*”的场景下是需要我们去保持核心竞争力的。
上课时我进行了自我的反问。从前,我从不想将自己放在与人竞争的位置,只想尽量做好自己。至少在我看来,同人相比较是件不光彩的事,每个个体没有相同的轨迹和需要被比较的必要。但现在想来,在生活的时时刻刻我又何尝不是处在竞争的环境下呢?所以,做好自己并不去打扰别人的努力,自我奋斗去实现自身价值即可。至于对待结果的态度,从一而终。
二叉查找树这一章很有意思也不太容易完全理解。但是呢,事物的发展不是直线式前进的,而是螺旋式上升的。所以我们既要看到道路的曲折,也要看到前途的光明!学习进度条Learning List
代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 目标 5000行 30篇 400小时 第一周 0/0 1/1 8/8 第二周 621/621 1/2 12/20 第三周 678/1299 1/3 10/30 第四周 2734/4033 1/4 20/50 第五周 1100/5133 1/5 20/70 第六周 1574/6707 2/7 15/85 第七周 1803/8510 1/8 20/105