20172310 2017-2018《程序设计与数据结构》(下)第八周学习总结
20172310 2017-2018《程序设计与数据结构》(下)第八周学习总结
教材学习内容总结
1.1、堆
-
堆(heap):是具有两个附加属性的二叉树。一是堆是一颗完全树(如果一棵二叉树是平衡的,即所有叶子都位于h或h-1层,其中h为log2n, 且n是树中的元素数目,且所有h层中的叶子都位于该树的左边,那么该树就被认为是完全的。),二是对每一结点,它小于或等于(大于或等于)其左孩子和右孩子。
-
小于或等于其左孩子和右孩子的是最小堆(minheap),结点大于或等于它的左右孩子是最大堆(maxheap), 其中的结点大于或等于它的左右孩子。关键概念
-
最小堆将其最小元素存储在该二叉树的根处,且其根的两个孩子同样也是最小堆。
-
堆的接口:
1.2、addElement操作
- addElement方法将给定的Comparable元素添加到堆中的恰当位置处,且维持该堆的完全性属性和有序属性。所以完成addElement方法有两个步骤,一是将元素放入堆中,二是调整元素的位置,保持堆的性质。
- 放入的位置:一个堆就是棵完全树, 所以对于插入的新结点而言只存在一个正确的位置, 且它要么是h层左边的下一个空位置,要么是h+1层左边的第1个位置(如果h层是满的话)
- 排序属性:第一将该新元素和其父亲的值进行比较,如果该新结点小于其双亲则将它们互换;然后重复,沿着树向上,直至该新元素要么是大于其双亲要么是位于该堆的根处。在堆实现中,我们会对树中的最末个结点,进行跟踪记录。
- 放入的位置:一个堆就是棵完全树, 所以对于插入的新结点而言只存在一个正确的位置, 且它要么是h层左边的下一个空位置,要么是h+1层左边的第1个位置(如果h层是满的话)
1.3、 removeMin 操作
- 最小元素是存储在最小堆的根处的,所以要做的就是返回根元素并用堆中的另一元素替换它。
- 替换:与addElement操作一样,是要维持该树的完全性,那么只有一个能替换根的合法元素,那就是是存储在树中最末片叶子上的元素。
- 重排:将该新根元素与其较小的孩子进行比较,且如果孩子更小则将它们互换。沿着树向下继续这过程,直到该元素要么位于某叶子中, 要么比它的两个孩子都小。
1.4、findMin操作
- 堆中最小元素元素总是被存储在该树的根处,所以实现findMin方法只需通过返回存储在根处的元素。
2.1、使用堆: 优先级队列
- 优先级队列(priority queue) 是遵循两个排序规则的集合。
- 具有更高优先级的项目在先。
- 具有相同优先级的项目使用先进先出方法来确定其排序。
- 优先级队列实现方式有两种使用队列列表或最小堆。虽然最小堆根本就不是一个队列,但是它却提供了一个高效的优先级队列实现。
- 过程:创建个PriorityQueueNode对象,用来存储将被放置在队列中的元素、该元素的优先级、以及元素放进队列的顺序。然后,使用compareTo 方法,对优先级进行比较,然后在优先级相同的时候再对阶进行比较。
3、用链表实现堆
- 因为我们要求在插入元素后能够向上遍历该树,所以堆中结点必须存储指向其双亲的指针。
- 堆接口中定义的方法用链表实现,实现的算法和逻辑就是上述所写的。
- 链表实现中实例化一个HeapNode类的LastNode引用,来跟踪记录堆中的最末一片叶子。
- addElement方法还调用了两个私有方法来实现上述的两个步骤,其操作的复杂度为2×log n +1+log n ,即O(log n)。
- addElement方法中调用的第二个重排序的方法实际上并没有执行双亲与孩子的完整互换,它只是把双亲元素往下平移到正确的插入点,其复杂度也是O(log n)。但是,它的确提高了效率,因为它减少了在堆的每一层上要执行的赋值次数。
- removeMin方法同理,时间复杂度为2Xlogn+logn+1,即O(log n)。
- findMin方法的时间复杂度为O(1)。
4、用数组实现堆
- 在二叉树的数组实现中,树的根位于位置0处,对于每一结点n, n的左孩子将位于数组的2n+1位置处,n的右孩子将位于数组的2(n+ 1)位置处,因此我们能够计算
双亲和孩子的位置。 - 数组实现的addElement方法必须首先检查可用空间,如需要,要对该数组进行扩容。
- 与链表实现不同,数组实现不需要确定新结点双亲的步骤。
- 数组实现的addElement操作的时间复杂度与链表实现的复杂度一样为1+log n或O(log n)。
- 链表实现和数组实现的removeMin操作的复杂度同为O(logn),
- findMin 操作与链表实现的一样, 其复杂度为O(1)。
5、使用堆:堆排序
- 使用一个堆来对某个数字列表进行排序,只需将列表的每元素添加到堆中, 然后一次一个地将它们从根中删除。 在最小堆的情形下,排序结果将是该列表以升序排列。在最大堆的情形下,排序结果将是该列表以降序排列。
- 对于堆的排序算法,我们需要执行addElemcmt和removeElement两个操作n次,即列表中每个元素一次。 因此,最终的复杂度为2XnXlogn,即O(nlog n)。
蓝墨云上做的一个练习就很清楚地显示了这个过程。
教材学习中的问题和解决过程
-
问题1:课本多次说到完成一些操作例如:addElement和removeMin等操作,数组和链表实现的堆的复杂度是相同的,但是还是说数组实现的效率更高,这是为什么?数组和链表实现堆的优缺点分别是什么?
-
问题1解决方案:
用数组实现堆时,进行addElement操作不需要和链表一样重新确定新节点双亲,removemin操作不需要确定新的最末结点。
简单总结一下,用数组实现堆的好处有
优点:和顺序存储一样的
1、可以很方便找到孩子、双亲和兄弟,以及祖先和子孙
2、只需要存储数据元素,不需要另外存储元素的逻辑关系
3、物理结构和逻辑结构一致
- 问题2:代码解读。下面这一段代码是用链表实现堆的addElement操作时的返回插入结点双亲的那个节点的引用,但是我分情况讨论的过程中一直出现理解不了的地方。
private HeapNode<T> getNextParentAdd()
{
HeapNode<T> result = lastNode;
while ((result != root) && (result.getParent().getLeft() != result))
result = result.getParent();
if (result != root)
if (result.getParent().getRight() == null)
result = result.getParent();
else
{
result = (HeapNode<T>)result.getParent().getRight();
while (result.getLeft() != null)
result = (HeapNode<T>)result.getLeft();
}
else
while (result.getLeft() != null)
result = (HeapNode<T>)result.getLeft();
return result;
}
- 问题2解决方案:第一遍看的时候,把自己的书画的乱七八糟的都没有理解这些代码的意思,
然后就去问馨雨同学,这些代码的意思,结果讨论讨论着,馨雨馨雨就被我问蒙了,O(∩_∩)O,后来,发现是我们理解错了lastNode结点,“插入结点的双亲结点”,之前一直理解的是将要插入的结点的双亲结点,其实lastNode是最后一片叶子结点,情况如下:
代码调试中的问题和解决过程
- 问题1:
ArrayHeap类的代码书上是有给出的,然后自己写了一个测试类,结果发现出现了这样的问题
- 问题1解决方案:想要进行调试看一下,结果发现
(这个问题我不知道是为什么发生的o(╥﹏╥)o)
然后又仔细思考了一下代码,在代码中将最后一个元素赋值给了root结点,但是原因是并没有将叶结点删除,所以接下来重新调平衡的时候,最后的那个位置并进行操作,所以出现了重复。
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
- 错题1及原因,理解情况
What type does "compareTo" return?
A
.
int
B
.
String
C
.
boolean
D
.
char
解析:compareTo()方法确实是String类中的一个,但是compareTo()的返回值是int。
它先比较对应字符的ASCII码,如字符串的某字符与参数的某字符不等,则结束比较,返回它们ASCII码的差值。直至字符串的字符或参数的字符 有一方全比较完,此时比较字符串的长度差并返回。
结对及互评
点评:
-
本周结对学习情况
- 2017-2018-20172309 《程序设计与数据结构》第八周学习总结
- 结对学习内容
- 堆的学习
- 实验二的探讨
-
博客中值得学习的或问题:
挺逗的,可以用来吸引目光。博客的页面又进行了修改,挺好看的。
-
代码中值得学习的或问题:
- 设计的思路很清晰,不像我一样总是很奇葩,_。
点评过的同学博客和代码
- 上周博客互评情况
其他(感悟、思考等,可选)
这周又是Java课多到爆表的双周,我们又开始完成实验二了,而且这周老师还让我们组了个队,要完成一个Java项目的开发,ε=ε=ε=(#>д<)ノ,难。
这个学期越来越注重代码的编写能力,也接触到了很多的新的算法和知识点,就像这个礼拜学的堆,希望自己接下来的课程要继续努力。
学习进度条
|| | 代码行数(新增/累积)| 博客量(新增/累积)|学习时间(新增/累积)|重要成长|
| -------- | :----------------😐:----------------😐:---------------: |:-----😐
| 目标 | 5000行 | 30篇 | 400小时 | |
| 第一周 | 0/0 | 1/1 | 10/10 | |
| 第二周 | 326/326 | 1/2 | 18/28 | |
| 第三周 | 784/1110 | 1/3 | 25/53 | |
| 第四周 | 2529/3638 | 2/5 | 37/90 | |
| 第五周 | 1254/4892 | 2/7 | 20/110 | |
| 第六周 | 1403/6295 | 2/9 | 32/142 | |
| 第七周 | 1361/7656 | 1/10 | 35/177 | |
| 第八周 | 2750/10406 | 2/12 | 32/209 | |
-
计划学习时间:30小时
-
实际学习时间:32小时
-
改进情况:这周是双周,Java课比较多,课程内容也比较多,保持了良好的学习进度和时间。