算法导论第6章习题

Exercises

6.1-1 2^h~2^{h+1}-1,如果root是第1个,而不是第0个。

 

6.1-2 设heap高度为h,2^h<=n<=2^{h+1}-1,取对数得到\floor{lgn}

 

6.1-3 传递性, Cormen用的是反证法

 

6.1-4 因为smallest element在heap中没有children,所以只能在leaf

 

6.1-5 YES. 在已排序的数组里A[i]的children A[2*i]和A[2*i+1]在A[i]后,所以两个children比A[i]小,所以是min-heap。

 

6.1-6 No.

 

6.1-7 用图中的出度=入度计算。废话了点。如果n是奇数,出度=2*#中间节点;入度=#中间节点-1(root没有入度)+#叶子节点。得到#中间节点=#叶子节点-1。总共有n个节点,#中间节点=(n-1)/2。所以第一叶子的index是(n+1)/2=\floor{n}/2。如果是n是偶数,#中间节点=#叶子节点,所以叶子节点从n/2+1=\floor{n/2}+1开始。

 

6.2-1 略

 

6.2-2 MIN-HEAPIFY(A, i, n) 把MAX-HEAPIFY中的Line3-7改成寻找最小的element,O(lgn)

 

6.2-3 MAX-HEAPIFY中的Line8 if语句不成立,函数执行完毕

 

6.2-4 i>heap-size[A]/2时,由6.1-7知A[i]是叶子节点,没有children,所以MAX-HEAPIFY在Line3跳出

 

6.2-5 好的编译器可以实现尾递归。伪代码:

MAX-HEAPIFY(A, i)

while i < floorA.heap-size/2      
      l=LEFT(i); r=RIGHT(i); 
      if A[l] > A[i]
      	 largest=l;
      else 
      	 largest=i;
      if A[r] > A[largest]
         largest=r;
      if largest!=i
         exchange A[i] with A[largest]
         i = largest;
 

6.2-6 对root用MAX-HEAPIFY,如果root值是heap中的最小元那么MAX-HEAPIFY一直会执行碰到一个leaf。因为总共有\Theta(lgn)层,所以时间复杂度是\Theta(lgn),worst-case的时间复杂度\Omega(lgn).

 

6.3-1 略

 

6.3-2 BUILD-MAX-HEAP中Line2为什么从\floor(n/2)递减至1,而不是递增?BUILD-MAX-HEAP中间要对A[i]要调用MAX-HEAPIFY,前提A[i]的两个children必须是max-heap的root。如果BUILD-MAX-HEAP中改为递增后,A[i]的两个children都不一定是max-deap。

 

6.3-3 证明在高度为h的节点中最多有\ceiling{n/2^{h+1}}个节点。一开始这个题很容易与depth深度混淆。如果heap不是一棵完整的二叉树的话,那么有一层的节点不都有高度h。高度heigh是从leaf算起,而depth从root算起。

如果是完全二叉树,结论显而易见。如果不是的话,证明就没有那么简单了。CLRS中有很多看似想当然的东西其实很有难度的题目。用数学归纳法(CLRS中证明算法的correctness都是用loop invariant跟数学归纳法一回事)。

H=0时的节点都是leaf,由前面的6.1-7得到n为偶数是,internal和leaf数量相等,所以确实有n/2=\ceiling{n/2}。n为奇数时,internal node比leaf少一个,所以leaf的数量=\ceiling{n/2}。

假设有H=h的节点有N_h=\ceiling{n/2^h}个。在这棵不完全二叉树中把H=h, h-1, h-2, …, 0高度的节点去掉,剩下n’个节点。那么H=h-1高度的节点都是叶子节点,那么,H=h-1的节点数目为\ceiling{n’/2}<=\ceiling{(#H=h的节点)/2}<=\ceiling{n/2^{h+1}}.QED.

 

6.4-1 6.4-2 略

6.4-3 increasing order: O(n)+n*O(lgn)=O(nlgn); decreasing order: O(1)+n*O(lgn)=O(nlgn) [只计算交换元素的步骤?]

 

6.4-4 worst-case running time: 在这里用decision-tree来说heapsort有\Omega(nlgn)时间复杂度都是偷懒。worst-case heapsort发生在root与A[heapsize+1]交换时MAX-HEAPIFY要使root下降到最下一层。由6.2-6知道,MAX-HEAPIFY worst case是\Omega(lgn),所以heapsort worst case是\Omega(nlgn)

 

6.4-5 这是最难的一道关于heap的题目了。Heapsort提出在1964,但是分析却是在1992年.Schaffer and Sedgewick's "The Analysis of Heapsort" paper.(Sedgewick是Knuth的高徒,也写过一本非常不错的算法书)其中的证明参考一篇stackoverflow的帖子。CLRS为什么要出这样的题呢,因为很多人都没有注意到一个情况:MAX-HEAPIFY best-case只要O(1)就够了,所以“有可能”heapsort只要O(n)就够了。(不要用decision tree来扯)。那这种情况会不会在MAX-HEAP上发生呢?[真讨厌latex画图,Win的机子也没有装latex,ASCII将就一下。]虽然heapsort是棵近似的完全二叉树,左右节点的高度不会超过1.但是那些较小的元素有可能不在leaf节点上。这样的话在用MAX-HEAPIFY的时候,新的root有可能下降到中间几层。比如

        7

   3        6

1    2    4   5

这里的3在第1层。如果把5换到7的位置,那么5只要下降1层就够了。这个例子自己认为是"best heap"。

Stackoverflow那个帖子倒数两段有2个地方笔误了,也没有讲不是完全二叉树的情况。拿完全二叉树知道大概意思吧:先假设一个有n=2k-1节点组成的max-heap,完全二叉树,高度k,深度从0到k-1。在heapsort中做的工作相似,我们把node一个一个从A[1]里取出来,把最后的元素放进去,MAX-HEAPIFY一下。考虑从中取出\floor{n/2}个node,记为集合X。他们中部分node会分布在原来max-heap中0-k-2层,记为X_1,部分node在leaf中,记为X_2。X_1集合中的node要向从heap中弹出来,他们每次只能向上走一层。所以我们只要计算X_1集合中的node就能得到heapsort的下界(包括worst-case和best-case)。

首先我们要知道总共有多少个最大的2^{k-1},即最大的n/2个节点有多少在最底层。答案是最多有2^{k-2}个。在上面这个例子中就是4和5在bottom level。这个很绕的证明先不管,如果是的话,至少有2^{k-2}个在0~k-2层中,也就是除掉最低层的其他层。那么,k-3层最多有2^{k-3}个,那么这些点中最多有2^{k-3}个在0~k-3层中(这个地方也比较跳跃,估计是自己太菜的缘故,最后一段补充),那么至少有2^{k-3}个X_1的点在k-2中,即倒数第2层中。这些节点到root的距离之和为(k-2)2^{k-3},k=\Theta(lgn),所以要使得这些点跑到root里要\Theta(nlgn)。因为我们只考察了一半的node,所以heapsort至少要\Omega(nlgn)。

再讲中间那个证明:如果在n/2最大的node(那些大于数组中位数的点)中有至少2^{k-2}+1个在最底层。那么这些node的大parent将会在k-2层。这样的parent至少有2^{k-3}+1个。以此类推,一直到0层root,那么这些大的node至少有2^{k-1}+k个大于中位数的node。这是不可能的,顶多有2^{k-1}个大于中位数的点。

终于完了。在不完全二叉树的证明看原始论文吧。结论是heapsort的best-case的时间复杂度是~1/2*nlgn。

自己的注解: k-2层由最大n/2个节点集合X和剩下的点集合A-X构成。因为在最底层的X集合的点要比A-X的点大,所以他们的parent必然也是X集合的点。如果0~k-3层多于2^{k-3}个节点多1,那么k-2层得节点只有2^{k-3}也要少1个,那么最底层k-1层的X集合的点也要减少。所以X集合中点的个数减少了,矛盾。

现在又想到一个直观的方法。"best"heap是尽量使得大的node靠近底层,他们的parent自然也是大的点。如果用小的点替换root,使它下降的话,那么只要走很少的层数就够了。把max-heap看成一个三角形,左边一半x是最大的n/2个node,右边一半是最小的n/2个node。如果有比这更“好”的heap的话,那么x的一个node要移到Y中,Y中有一个node要移到X中,但是这违反了max-heap。这样一个heap中要让左边的X节点从root中出去,必然还是要上升\Theta(lgn)的层数,共有n/2个,那么时间复杂度至少\Omega(nlgn)。

image

 

6.5-1 6.5-2 6.5-3 略

 

6.5-4 没有练code的原因没想到。因为heap是用数组来表示,当delete一个node是仅仅是把A[1]放到数组后面去,他的key可能还没有改变。所以用-infinity赋值一下来消除未初始化的情况。否则HEAP-INCREASE-KEY可能会出错。

 

6.5-5 略

 

6.5-6 CLRS第3版新加了一道题。要求用插入排序insertion sort中inner loop使得HEAP-INCREASE-KEY中Line5中的3次赋值变为1次赋值。一般来说exchange要用到3个变量,c=a,a=b,b=c。先把那些小的元素下降到它的children,然后把key插入到正确的位置。

HEAP-INCREASE-KEY(A, i, key)
if (key < A[i])
  Error;
while (i>1 and A[PARENT(i)] < key)
  A[i]=A[PARENT(i)];
  i=PARENT(i);

A[i]=key;

 

6.5-7 实现FIFO queue。只要使得先来的element大于heap的MAXIMUM即可。这是用堆实现的queue。

 

6.5-8 实现HEAP-DELETE(A, i)

def HEAP-DELETE(A, i):
    A[i]=A[A.heap-size]
    A.heap-size=A.heap-size-1
    MAX-HEAPIFY(A,i)

 

6.5-9 O(nlgk),用这k个list的第1个元素构成有k个node的min-heap。每次从中抽取最小的node,加入这个node所在list中的下面一个node,直到排序完所有元素。

 

Problems

6-1 Building a heap using insertion 用插入方法建堆

a. No, 显而易见

b. BUILD-MAX-HEAP’在worst-case要\Theta(nlgn)的时间复杂度,明显多于标准的BUILD-MAX-HEAP的O(n)。证明\Theta时间复杂性要从上界和下界同时证明。上界易得:MAX-HEAP-INSERT要O(lgn),所以BUILD-MAX-HEAP’要O(nlgn)。下界:worst-case是数组按升序排列,每一次MAX-HEAP-INSERT需要从leaf跑到root。

image

6-2 Analysis of d-ary heaps 对d叉堆的分析

a. to children: D-ARY-CHILDREN(i,j): d(i-1)+j+1; D-ARY-PARENT(i): \floor{(i-2)/d+1}

b. \Theta(\log_d n)

c. like HEAP-EXTRACT-MAP,但是在MAX-HEAPIFY中要比较i和它d个children,worst-case时间复杂度\Theta(d log_d n)

d. MAX-HEAP-INSERT, worst-case时间复杂度\Theta(\log_d n)

e. D-ARY-INCREASE-KEY也是同样地比较节点与parent的大小,可以采用6.5-6(第3版新题)优化方法。worst-case时间复杂度还是Theta(\log_d n)

6-3 Young tableaus Young氏矩阵

a. Young tableaus可以用Z折线从小到大排列出来,X表示正无穷大

2   4   5   X

3   8   16 X

9   14  X  X

12  X   X  X

b. 如果Y[1][1]是正无穷大的话,那么对于任何一个元素Y[x][y]都有一条路Y[x][y] >= Y[x][y-1] >= … >= Y[x][1] >= Y[x-1][1] >= … >= Y[1][1] >= 正无穷大,所以Y[x][y]是正无穷大,Young tableaus为空。同理,若Y[m][n]=M<正无穷大,那么也用这样的一条路使得Y[x][y]<=M, x<=m, y<=n,所以Young tableaus为满。

c. EXTRACT-MIN(Y)

def EXTRACT-MIN(Y):
    minimum = Y[1][1]
    Y[1][1] = INFINITY
    YOUNG-TABLEAUSIFY(Y, 1, 1)
    return minimum

def YOUNG-TABLEAUSIFY(Y, m, n):
    smallest = Y[m][n]
    if u <= Y.M and Y[m+1][n] < smallest:
        smallest = Y[m+1][n]
        u = m + 1
		v = n
    if v <= Y.N and Y[m][n+1] < smallest:
        smallest = Y[m][n+1]
        v = n + 1
		u = m
    if not(u == m and v == n):
       Y[u][v] = Y[m][n]
       Y[m][n] = smallest
       YOUNG-TABLEAUSIFY(Y, u, v)

T(p)=T(m+n)=T(m+n-1)+O(1)=O(m+n)

d. YOUNG-TABLEAUS-INSERT(Y, m, n)

如果Young tableaus为Y[m][n]若为∞,则Y[m][n]=x, 否则,返回full。找出Y[m-1][n]和Y[m][n-1],Y[m][n]中最小的一个,与其交换,保证不要越界。如果自己与自己交换则停止,否则的话,YOUNG-TABLEAUS-INSERT(Y, u, v),u,v是所交换元素的坐标。一直到停止或者到了Y[1][1]。所以也是O(m+n).

e.不断的EXTRACT-MIN(Y), n*n*O(2n)=O(n^3)

f. IS-IN-YOUNG(Y, key) 只要把key和Y最右上角元素x比较就可以了。x刚好是第一行和最后一列的中位数,是第一行的最大值,最后一列的最小值。如果key比x大,那么去掉第一行。如果key比x小,去掉最后一列,继续搜索。直到找到或者没找到为止。每次必然减少一行或者一列,所以时间复杂度是O(m+n)。

据说这个还是某度的面试题,不过那里的矩阵是方阵,没有∞元素。

转载于:https://www.cnblogs.com/starry4d/archive/2011/08/04/2126925.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值