zoukankan      html  css  js  c++  java
  • 《算法导论》第六章----堆排序练习(证明)(完整版)

    《算法导论》学习记录目录

    欢迎指出其中的错误和交流。

    关于堆排序的具体介绍和C代码实现见该链接

    算导关于堆排序的练习主要是一些证明,可以帮助理解堆的特征。部分练习是图示过程,这些练习认真用笔过一次会很有收获。

    1.在高度为h的堆中,最多和最少的元素个数是多少?

    最多:底层全满;1 + 2^1 + 2^2 + ...... + 2^h,等比数列求和得2^(h+1) - 1

    最少:底层只有一个节点;1 + 2^1 + 2^2 + ...... + 2^(h-1) + 1,等比数列求和得2^h - 1 + 1 = 2^h

    2.证明:含n个元素的堆的高度为floor(lgn)

    假设n个元素的堆的高度为h。由上题得2^h <= n <= 2^(h+1) - 1,因此h <= lgn < h+1。

    根据floor 和 ceiling 函数的性质

    维基

    得h = floor(lgn)

    3.证明:在一个最大堆的某棵子树中,最大元素在该子树的根上。(看了答案才知道用反证法,居然想不到反证法,离散数学白看了。。。。。)

    假设这个命题为错误的,存在一棵子树的最大元素不在该子树的根,最大元素的下标为m。

    则下标为m的节点的值比其父节点的值大,但是最大堆的特性为某个节点的值最多和其父节点的值一样大,矛盾,因此假设为错误,命题正确。

    4.证明:当用数组表示存储了m个元素的堆时,叶节点的下标是floor(n/2)+1, floor(n/2)+2,...n

    刚开始打算通过公式证明最有一个叶节点的父节点为floor(n/2),最后没能成功(数学太渣)。。。

    根据二叉堆的性质:某节点下标为i(非根节点),其父节点的下标为floor(i/2),因此最后一个叶节点的父节点的下标为floor(n/2),所以从下标floor(n/2)+1开始到n都是叶节点。

    5.写出最小堆中维持最小堆性质的操作(本来要求写伪代码)

     1 void min_heapify(int A[], int length, int i){
     2     int l = 2 * i;
     3     int r = 2 * i + 1;
     4     int smallest;
     5 
     6     if(l <= length && A[l] < A[i])
     7         smallest = l;
     8     else
     9         smallest = i;
    10     if(r <= length && A[r] < A[smallest])
    11         smallest = r;
    12 
    13     if(smallest != i){
    14         int temp = A[smallest];
    15         A[smallest] = A[i];
    16         A[i] = temp;
    17         min_heapify(A, length, smallest);
    18     }
    19 }
    View Code

    6.将max_heapify()函数的递归调用改为迭代结构,使效率提高。

     1 void max_heapify(int A[], int length, int i){
     2     int l, r;
     3     int largest, temp;
     4     while(i <= length){
     5         l = 2 * i;
     6         r = 2 * i + 1;
     7 
     8         if(l <= length && A[l] > A[i])
     9             largest = l;
    10         else
    11             largest = i;
    12         if(r <= length && A[r] > A[largest])
    13             largest = r;
    14         
    15         if(largest != i){
    16             temp = A[largest];
    17             A[largest] = A[i];
    18             A[i] = temp;
    19             i = largest;
    20         }
    21         else
    22             break;
    23     }
    24 }
    View Code

     7.证明:对于一个大小为n的堆,max_heapify的最坏运行时间为Ω(lgn)。(提示:对于n个节点的堆,恰当地设置每个节点的值,使得从根节点到叶节点的路径上的每个节点都递归调用max_heapify)。

    假设大小为n的堆的高度为h

    首先如何使得从根节点到叶节点的路径上的每个节点都递归调用max_heapify?每次调用后的子树都符合子树的根节点小于左右儿子节点,则最初的根节点的值比所有左右儿子的值都小。接着要考虑的就是路径-----如何使得从根节点到叶节点的路径更长(最坏的情况)?“堆数据结构是一种数组对象,可以视为一棵完全二叉树。树的每一层都是填满的,最后一层除外(最后一层从一个节点的左子树开始填)”------《算导》原文。因此每次根节点与其左儿子节点交换,路径会是最长的,则所有左子树的值大于或等于右子树。这样设置,max_heapify会调用h次,所以运行时间为Θ(h),即Θ(lgn)。根据大Θ符号的定义(链接为维基),详细见算导第三章----函数的增长,因此的得最坏运行时间为Ω(lgn)。

    8.证明:在任一含n个元素的堆中,最多有ceiling(n / 2^(h+1))个高度为h的节点。

    证明为算导的答案:(答案很详细)

    9.对于一个其所有n个元素已按递增序排列的数组A,堆排序的运行时间是多少?若A的元素呈降序呢?

    递增序排列和递减序(降序)一样:先建堆,运行时间为O(n),然后将数组最后一个与第一个交换,数组大小减一,在进行max_heapify(维持最大堆的性质),依次重复操作(O(n) * O(lgn) = O(n*lgn))

    因此运行时间为O(n) * O(lgn) = O(n*lgn)

    10.证明:堆排序的最坏情况运行时间为Ω(n*lgn)。

    堆排序属于比较排序,比较排序的最坏情况运行时间的下界为Ω(n*lgn),见算导第八章第一节。

    11.证明:在所有元素都不相同时,堆排序的最佳运行时间为Ω(n*lgn)。

    无论数组是否已排序,都要调用n - 1次max_heapify函数,可以得最佳运行时间为Ω(n*lgn)。

  • 相关阅读:
    Jquery所有获取对象
    使用VS Code 调试Vue
    Http请求
    Xml,Json序列化
    SqlServer函数使用
    FastReport关闭打印提示框
    求面试经验
    pyspark基于python虚拟环境运行
    idea配置本地spark本地开发环境
    carbondata使用总结
  • 原文地址:https://www.cnblogs.com/alan-forever/p/3324784.html
Copyright © 2011-2022 走看看