20172311《程序设计与数据结构》第七周学习总结
教材学习内容总结
第十一章 二叉查找树
- 二叉查找树是一种含有附加属性的二叉树,即其左孩子小于父节点,而父节点又小于等于右孩子
- 二叉查找树的UML描述
- addElement操作
removeElement操作
- 用有序列表实现二叉查找树体现了树为其他集合提供高效实现的用途
有序列表的链表实现分析和二叉查找树实现分析
- 如果二叉查找树不平衡,其效率可能比线型结构的还要低。可以通过左旋、右旋、左右旋、右左旋解让树平衡化
左旋
右旋
左右旋
右左旋
- AVL树是树的一种变体,对于树中的每个节点我们都会跟踪记录其左右子树的高度,对于书中任何结点如果其平衡因子(即右子树高度减去左子树高度)——大于1或者小于-1,则以该节点为树根的子树需要重新平衡。
- 树(或树的任何子树)只有两种途径能变得不平衡:插入节点或删除节点
AVL树的右旋
AVL树的右左旋
二叉查找树的另一种实现是红黑树,红黑树是一种平衡二叉查找树,其中每个节点存储一种颜色(红色或黑色,通常用一个布尔值来实现,值false等价于红色)。控制节点颜色的规则如下
1、每个结点或是红色的,或是黑色的
2、根节点是黑色的
3、每个叶结点(NIL)是黑色的
4、如果一个节点是红色的,则它的两个儿子都是黑色的。
5、对于每个结点,从该结点到其叶子结点构成的所有路径上的黑结点个数相同。- 某种程度上,红黑树中的平衡限制没有AVL树那么严格。但是,它的序仍然是logn。
红黑树示意图如下:
教材学习中的问题和解决过程
- 问题1:AVL树与红黑树之间的联系与区别在哪
- 问题1解决方案:
- 红黑树与AVL树的比较:
1.AVL是严格的平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;
2.红黑树是用非严格的平衡来换取增删节点时候旋转次数;
3.所以如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL树,如果搜索,插入删除次数几乎差不多,应选择红黑树。即,有时仅为了排序(建立-遍历-删除),不查找或查找次数很少,R-B树合算一些。
4.红黑树与AVL树的调整平衡的实现机制不同,AVL靠平衡因子和旋转,红黑树靠节点颜色以及一些约定再加上旋转。因此,存在去掉颜色的红黑树后它不是AVL树,比如左子树都是黑的,右子树都是红黑相间的,这样整个树高度2n的时候,根节点的左右层数差可以到n。
红黑树(RB-tree)比AVL树的优势在哪?
红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高。红黑树能够以O(log2 n) 的时间复杂度进行搜索、插入、删除操作。红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高。因此,如果你的业务中查找远远多于插入、删除,那选AVL树;如果查找、插入、删除频率差不多,那么选择红黑树。
问题2:无法理解课本上给出的红黑树的插入操作
问题2解决方案:查阅资料总结如下
插入过程
默认插入的结点为红色。为何?
因为红黑树中黑节点至少是红节点的两倍,因此插入节点的父节点为黑色的概率较大,而此时并不需要作任何调整,因此效率较高。
1. 父为黑
插入后无需任何操作。由于黑节点个数至少为红节点的两倍,因此父为黑的情况较多,而这种情况在插入后无需任何调整,这就是红黑树比AVL树插入效率高的原因!
2. 父为红
父为红的情况破坏了红黑树的性质,此时需要根据叔叔的颜色来做不同的处理。
1.叔叔为红
此时很简单,只需交换爸爸、叔叔和爷爷的颜色即可。
此时若爷爷节点和太爷爷节点颜色相同,再以爷爷节点为起始节点,进行刚才相同的操作,即:根据爷爷的兄弟颜色做相应的操作。
2.叔叔为黑
此时较为复杂,分如下四种情况:
a)爸爸在左、叔叔在右、我在左
以爸爸为根节点,进行一次R旋转。
b)爸爸在左、叔叔在右、我在右
先以我为根节点,进行一次L旋转;
再以我为根节点,进行一次R旋转。
c)叔叔在左、爸爸在右、我在左
先以我为根节点,进行一次R旋转;
再以我为根节点,进行一次L旋转
d)叔叔在左、爸爸在右、我在右
以爸爸为根节点,进行一次L旋转。
代码调试中的问题和解决过程
- 问题1:链式二叉查找树中
findMax
方法运行时出现空指针异常
运行截图如下:
方法代码及报错位置截图如下:
- 问题1解决方案:经过debug调试发现是逻辑错误问题,通过与
findMin
方法对比改正后代码如下:
//返回二叉查找树中的最大元素
@Override
public T findMax() {
T result = null;
if (isEmpty())
throw new EmptyCollectionException("LinkedBinarySearchTree");
else {
if (root.getRight() == null) {
result = root.getElement();
} else {
BinaryTreeNode<T> current = root.getRight();
while (current.getRight() != null) {
current = current.getRight();
}
result = current.getElement();
}
}
return result;
}
}
代码托管
上周考试错题总结
上周无错题!!!
结对及互评
- 本周结对学习情况
本周主要二叉查找树进行了较为深入的学习,在对链式二叉查找树学习时问题不多,但是当学到了AVL树时就遇到了较大的麻烦,问题多了起来,但是通过查阅资料以及与结对伙伴的讨论,最终使问题得到了较好的解决,通过结对编程,我们的学习效率得到了明显提升,希望以后更加默契,共同努力,使自己的编程能力得到更大的提高!
感想
乐观向上!灵活变通!永不言弃!坚持努力!
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 4/4 | |
第二周 | 464/464 | 1/2 | 10/14 | 理解掌握了用数组和链表实现栈的方法 |
第三周 | 494/958 | 1/3 | 10/24 | 理解掌握了用数组和链表实现队列的方法 |
第四周 | 1629/2587 | 2/5 | 20/44 | 对用链表和数组实现列表进行了学习 |
第五周 | 856/3443 | 2/7 | 15/59 | 较为深入的学习了查找和排序方法的实现 |
第六周 | 668/4111 | 1/8 | 20/79 | 学习了链式二叉树的实现 |
第七周 | 900/5011 | 1/9 | 15/99 | 对二叉查找树进行了较为深入的学习 |
计划学习时间:20小时
实际学习时间:15小时
改进情况:在今后的学习中遇到问题,首先要自己努力解决,如果没有思路,不能一直钻牛角尖,要学会 查阅资料,吸取别人的经验,学会扬弃,从而提高自己的学习效率。