20172313 2018-2019-1 《程序设计与数据结构》第八周学习总结

20172313 2018-2019-1 《程序设计与数据结构》第八周学习总结

教材学习内容总结

    • 堆(heap)就是具备两个附加属性的一棵二叉树。特色:①最小堆是一棵彻底树。②最小堆对每一结点,它小于或等于其左孩子和右孩子。(最大堆的结点大于或等于它的左右孩子)
    • HeapADT的UML描述。
操做 说明
addElement 将给定元素添加到该堆中 
removeMin 删除堆的最小元素 
findMin 返回一个指向堆中最小元素的引用 
  • 最小堆结点的子树一样是最小堆,两个不一样的最小堆可能包含数据相同
  • addElement操做:将给定的元素添加到堆中的恰当位置,由堆的定义可知,这里的元素要可以进行比较,因此必须是Comparable类型的,若是不是,则会抛出异常。若是一棵二叉树是平衡的,即全部叶子都位于h或h-1层。其中h为log2^n,且n是树中的元素数目,由于堆是一棵彻底树,因此h层的叶子都位于该树的左边。因此插入结点时只有两种状况:①h层的最后一个位置。②h+1层的第一个位置。将新元素添加至堆的末尾后,保持树是彻底树,将该元素向根的地方移动,将它与父结点对换,直到其中的元素大小关系知足要求为止。
    • 在链表实现中,添加元素时首先要肯定插入结点的双亲。最坏的一种状况是从右下的最后一个叶子节点一直遍历到根,在遍历到堆的左下结点 。该过程的时间复杂度为2logn。下一步是插入节点(简单的赋值,这里的时间复杂度为O(1))。最后一步是将这棵树进行从新排序。由于从根到结点的路径长度为logn,因此最多须要进行logn此操做。所以使用链表实现时操做的复杂度为2*logn+1+logn。即O(logn)
    • 在数组实现中,添加元素时并不须要肯定新结点双亲的步骤,可是,其余两个步骤与链表实现的同样。所以,数组实现的addElement操做的时间复杂度为1+logn或O(logn)。虽然这二者实现的复杂度相同,但数组实现的效率更高一些。

  • removeMin操做:根据堆的定义可知,堆中的最小元素位于根结点,这时咱们要把该树储存在最末一片叶子中的元素移到根结点处,一旦元素移动,就要对该树进行从新排序:将该新根元素与较小的那个孩子进行比较,若是孩子更小则将它们互换。从上至下继续这一过程,直到该元素要么变成叶子结点,要么比它的两个孩子都小。
    • 在链表实现中,removeMin必须删除根元素,并用最后一个结点的元素来替换它(简单的赋值,时间复杂度为O(1))。下面要对该树进行从新排序,由于该树原先是一个堆,因此只须要跟较小的一边进行比较排序。由于从根到叶子的最大路径长度为logn,所以该步骤的时间复杂度为O(logn)。到此时,这棵树已经完成了,但在实际进行的过程当中,为了继续完成接下来的操做,咱们还要找到新的最末结点,最坏的状况是进行丛叶子到根的遍历,而后再从根往下到另外一叶子的遍历。所以,该步骤的时间复杂度为2logn。因而removeMin操做最后的时间复杂度为2logn+logn+1,即O(logn)。
    • 在数组实现中,removeMin也像链表实现的那样,只不过它不须要肯定最新的最末结点。所以,数组实现的removeMin操做的复杂度为logn+1。即O(logn)。
  • findMin操做:findMin操做较为简单,由堆的定义可知,直接返回根结点的元素便可。
  • 堆和二叉排序树的区别:
    • ①堆是一棵彻底二叉树,二叉排序树不必定是彻底二叉树;
    • ②在二叉排序树中,某结点的右孩子结点的值必定大于该结点的左孩子结点的值,在堆中却不必定;
    • ③在二叉排序树中,最小值结点是最左下结点,最大值结点是最右下结点。在堆中却不必定;
  • 使用堆:优先级队列:按照优先级从大到小进行排序,具备相同优先级的按照先进先出来进行排序。虽然最小堆根本就不是一个队列,可是它却提供了一个高效的优先级队列实现。
操做 说明
addElement 往树中添加一个元素 
removeElement 从树中删除一个元素 
removeAllOccurrences 从树中删除所指定元素的任何存在 
removeMin 删除树中的最小元素 
removeMax 删除树中的最大元素 
findMin 返回一个指向树中最小元素的引用 
findMax 返回一个指向树中最大元素的引用 
  • 用链表实现二叉查找树:每一个BinaryTreeNode对象要维护一个指向结点所存储元素的引用,另外还要维护指向结点的每一个孩子的引用。

教材学习中的问题和解决过程

  • 问题一:在使用数组实现最小堆时,若是要进行removeMin操做须要把根元素删去,把“count-1”处的元素放到索引为0的地方,那么“count-1”处的元素不是还放在原位吗?书上并无对该位置进行任何操做。
  • 问题一解决方案:当时看到这里的时候,我认为是删除元素时就不对“count-1”处的元素进行操做,由于删除元素后count变量会自减,到时候遍历的时候只遍历到count的位置,这个问题也就先放到这里了。可是当我作到pp12_8时却发现不是这样。当我删除了最小元素后,在遍历的时候依旧会把最后一个元素打印。以下图所示

      咱们能够很清楚的看到,5做为最后一个元素并无被删除,仍然会被打印,因为ArrayHeap继承的是ARRayBinaryTree类,我又查看了ArrayBinaryTree中的遍历方法。
public String toString() 
    {
        ArrayUnorderedList<T> tempList = new ArrayUnorderedList<T>();
        inOrder(0, tempList);

        return tempList.toString();
    }

  使用数组实现堆时使用中序遍历,打印数组中不为空的全部元素,因此在这里我以为书上的内容是有问题的,在ArrayHeap的removeMin()方法中应该添加吧“count-1”处的元素清空的操做。这样问题就得以解决了。html

public T removeMin() throws EmptyCollectionException
    {
        if (isEmpty())
            throw new EmptyCollectionException("ArrayHeap");

        T minElement = tree[0];
        tree[0] = tree[count-1];
        tree[count-1] = null;(新添加的操做)
        heapifyRemove();
        count--;
        modCount--;
        
        return minElement;
    }

  • 问题二:在看链表实现堆时,有这样一段话“heapifyAdd方法并无执行双亲与孩子的完整互换。它只是把双亲元素往下平移到正确的插入点,而后把新值赋给该物质。这并无真正改变算法的复杂度,即便执行完整的元素互换,其复杂度也是O(logn)。可是,它的确提升了效率,由于它减小了左堆的每一层上要执行的赋值次数。”我对于这段话不是很理解。
  • 问题一解决方案:我是这样理解的,先用temp储存插入节点的元素,与父结点内储存的元素进行比较,若是父结点的元素大于temp,则把父结点的元素赋给子结点。注意:此时并无把temp的值赋给父结点,没有执行完整的元素互换。这时继续进行遍历,找到父结点的父结点,让父结点的父结点的元素与temp进行比较,重复以上步骤,直到根结点或有一结点的元素小于temp,这时,该结点的子结点就是新插入元素的位置,即把temp赋给它。因为没有进行完成的元素互换,效率天然也就提升了。

代码调试中的问题和解决过程

  • 问题1:对使用数组实现堆中的removeMin操做的heapifyRemove方法不是特别理解。代码以下:
private void heapifyRemove()
    {
        T temp;
        int node = 0;
        int left = 1;
        int right = 2;
        int next;
        
        if ((tree[left] == null) && (tree[right] == null))
            next = count;
        else if (tree[right] == null)
            next = left;
        else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
            next = left;
        else
            next = right;
        temp = tree[node];

        while ((next < count) && 
            (((Comparable)tree[next]).compareTo(temp) < 0))
        {
            tree[node] = tree[next];
            node = next;
            left = 2 * node + 1;
            right = 2 * (node + 1);
            if ((tree[left] == null) && (tree[right] == null))
                next = count;
            else if (tree[right] == null)
                next = left;
            else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
                next = left;
            else
                next = right;
        }
        tree[node] = temp;
    }
  • 问题一解决方案:咱们先来一行一行的逐步分析代码,咱们知道此方法是用来对堆进行从新排序的。首先,判断删除最小元素后是否只剩下一个结点,若是是,则跳事后面判断和循环,将剩下的惟一元素设置为根结点。有前面的内容可知,对于每一结点,n的右孩子将位于素组的2(n+1)处,n的右孩子将位于数组的2(n+1)处。若是对于根结点来讲,它没有右孩子或左孩子的元素小于右孩子的元素,则令next=left进入下一步的循环,若是它的右孩子的元素大于左孩子的元素。则令next=right进入下一步的循环。(注意:在判断结束后,temp= tree[node]的做用为保存此时根节点的元素向下遍历进行比较,最后赋值给经过循环找到的结点。)当进入循环之后,将根结点的元素与索引为next处的元素(左孩子或右孩子)进行比较,若是next处的元素比temp小,则令node(next的父结点)处元素为next。继续向下遍历,把next的值赋给node,并找到node的孩子的索引,令next从新指向node的孩子。当跳出循环时,node结点的位置即为temp的位置,完成排序。

代码托管

上周考试错题总结

  • 错题1:What type does "compareTo" return?
    A . int
    B . String
    C . boolean
    D . char
  • 解析:这题错的实在是不该该,当时作的太快了,满脑子都想着compareTo用来比较两个对象之间的大小,返回一个boolean型的变量,却忘了是根据返回int值的大小来判断的。
  • 错题2:Insertion sort is an algorithm that sorts a list of values by repetitively putting a particular value into its final, sorted, position.
    A . true
    B . false
  • 解析:概念题,插入排序经过反复地把某个元素插入到以前已排序的子列表中,实现元素的排序。
  • 错题3:A binary search tree is a binary tree with the added property that the left child is greater than the parent, which is less than or equal to the right child.
    A . True
    B . Flase
  • 解析:概念题,作的时候理解错误,二叉查找树是一种含有附加属性的二叉树,即其左孩子小于父结点,而父结点又小于或等于右孩子。
  • 错题4:One of the uses of trees is to provide simpler implementations of other collections.
    A . True
    B . Flase
  • 解析:这道题作的时候没有理解清楚题意。树的主要做用之意2是为其余集合提供高效的实现,而不是简单的实现。

结对及互评

  • 博客中值得学习的或问题:
    • 排版精美,对教材的总结细致,善于发现问题,对于问题研究得很细致,解答也很周全。
  • 代码中值得学习的或问题:
    • 代码写的很规范,思路很清晰,继续加油!

点评过的同窗博客和代码

  • 本周结对学习状况

其余(感悟、思考等,可选)

  相较与上周的折磨,这周的内容上能够说是友好了许多,经历了上周二插查找树的洗礼,这周的内容也只不过是新瓶装旧酒而已,没有什么太过新鲜的东西,主要是把一些关键的代码给弄明白,这一过程仍是花了很多时间的。因为这周的实验也占用了很多时间,这周的学习内容其实并无想象中那么轻松。但愿本身能在之后的生活中继续努力,不断进步!java

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
第一周 200/200 1/1 5/20
第二周 981/1181 1/2 15/20
第三周 1694/2875 1/3 15/35
第四周 3129/6004 1/4 15/50
第五周 1294/7298 1/5 15/65
第六周 1426/8724 1/6 20/85
第七周 2071/10795 1/7 20/105
第八周 3393/14188 1/8 20/125
  • 计划学习时间:20小时node

  • 实际学习时间:20小时git

参考资料

相关文章
相关标签/搜索