算法导论习题解答Chapter6(学习笔记)

6.1-1:

在高度为h的堆中, 元素最多为2^h, 元素最少为2^(h+1) - 1; 

理由如下:

最大堆可以理解为一个二叉树, 高度为h的2叉树最多有   2^(h+1) - 1个,最少的情况是在最后一层只有一个元素,则此时的元素个数为  2^h 个

 

 

6.1-2:

因为在高位h的堆中, 元素的个数在 2^h 个到   2^(h+1)-1 个之间, 所以如果元素的个数为n, 则高度一定为 lgn 向下取整,此时对数的底数是2

 

6.1-3:

因为在最大堆中, 所有结点都要满足, A[parent(i)] >= A[i], 所以对于最大堆中的任一子树, 其根结点的值应该大于该子数的左右孩子的值

 

6.1-4:

假设最大堆的所有元素不同,则最小的元素应该在最后一层的最后一个元素的位置上

 

6.1-5:

;已排好序的数组就是一个最小堆或者最大堆

 

6.1-6:

是一个最大堆

 

6.1-7:

在长度为n的数组中, 因为要对于每一个有孩子的结点都要满足,i*2 <= n, 所以对于倒数第二层的最后一个有子结点, 满足, i*2 <= n, 因为只有这样,才能满足其孩子在数组内, 所以对于其之后的数组中的元素,  都应该在叶子结点上, 并且其父亲都是   n/2+1 到 n 

 

6.2-1:

当i = 3时, A[3]违背了最大堆的性质, 因为其值不大于其孩子, 通过交换A[3] 和 A[6]  的值, 结点3恢复了最大堆的性质, 但又导致结点6违反了最大堆的性质, 递归调用MAX-HEAPIFY(A, 6), 此时i  = 6, 通过交换 A[6]和A[13]   的值, 使得 A[6]  恢复了最大堆的性质, 递归调用MAX-HEAPIFY(A, 13), 此时 i = 13, 此时不在有新的数据交换

 

6.2-2:

伪代码如下:

MIN-HEAPIFY(A, i):    //维护下标为i的结点作为根的子树 的最小堆的性质
    l = LEFT(i);  //记录左孩子的下标
    r = RIGHT(i); //记录右孩子的下标
    if l <= A.HEAP-SIZE and A[l] >= A[i]:
        minimum = i;
    else:
        minimum = l;
    if r <= A.HEAP-SIZE and A[r] <= A[minimum]:
        minimum = r;
    if minimum != i:
        exchange A[i] and A[minimum];
        MIN-HEAPIFY(A, minimum);
    

 

6.2-3:

当元素A[i] 的值比孩子的值都大时, 没有任何效果,但是此时不能保证 A[i] 的左右孩子都维持堆的特性

 

6.2-4:

当i > A.heap-size/2 时, 表明此时 A[i]  已经是叶子结点了, 所以肯定都维持了堆的特性

 

6.2-5:

迭代的算法如下:

MAX-HEAPIFY-CIRCLE(A, i):
    while(i < A.heap-size/2):
        //当i为叶子结点时退出
        l = A.left(i);          //记录左孩子的下标
        r = A.right(i);         //记录右孩子的下标
        if r <= A.heap-size and A[r] >= A[i]:
            largest = r;         //记录最大的值的下标
        else:
            largest = i;
        if l <= A.heap-size and A[l] >= A[largest]:
            largest = l;
        if i == largest:
            break;
        else:
            exchange A[i] and A[largest];

 

6.3-1:

循环开始时, i = 9/2向下取整得 i = 4, 执行MAX-HEAPIFY(A,i) 之后, A[i]为根得树维持了堆得性质, 然后i减一; i = 3 时, 通过将 A[3] 和 A[6]的 值互换之后, A[3]为根的树也维持了堆的性质, i减一, 此时 i  = 2, 通过执行MAX-HEAPIFY(A, i) 之后, 此时 A[2] 也维持了堆的性质, i减一 , 此时i = 1,  通过执行MAX-HEAPIFY(A, i) 之后, 已A[1] 为根的树也维持了堆的性质, i减一, 此时 i = 0, 退出循环, 而此时, 整个树已经维持了堆的性质

 

6.3-2:

 如果以1开始递增, 则在执行依次 MAX-HEAPIFY 之后, 以后每次执行完 MAX-HEAPIFY 之后不能保证整棵树保持堆的性质

 

 

6.4-1:

首先通过 BUILD-MAX-HEAP方法构建一个最大堆,然后每次将堆顶的数和最后一个堆中的最后一个数进行交换,交换之后堆的大小减一,交换之后可能导致当前堆不满足最大堆的性质, 因此每次都还需要调用 MAX-HEAPIFY 方法来保证每次交换之后堆都能保持是最大堆

 

6.4-2:

初始化: 当第一次循环开始时,即 i = A.length, 此时子数组 A[1, i] 就是原来的数组, 因为在第一句时已经将原来的数组设置成最大堆, 而因为剩余的元素是0个, 所以没有最大的元素在其中

保持: 因为第i-1 次循环结束之后, 都由 MAX-HEAPIFY 保证了当前的堆是最大堆, 为第i次循环奠定了基础, 同时,每次都是和堆顶的元素交换, 而堆顶的元素是当前堆的最大元素, 所以每次交换之后, 子数组 A[i+1, n] 包含了数组中已经排序的i-1个元素

终止:过程终止时, i = 1,  此时 子数组 A[1, 1] 只有一个元素, 所以此时  A[1, 1] 也是一个最大堆, 并且子数组 A[2, A.length] 中保存的都是已经排序的最大值,而此时堆顶元素是原数组中的最小元素, 所以最终保证了结论的正确性

 

 

6.5-3:

HEAP-MINIMUM   伪代码如下:

HEAP-MINIMUM (A):   
    return A[1];        //对于最小堆,返回堆顶元素就是最小元素

HEAP-EXTRACT0-MIN 伪代码如下:

HEAP-EXTRACT-MIN (A):
    if A.heap-size < 1:
        error "堆下溢"
    min = A[1];
    A[1] = A[A.heap-size];
    A.heap-size = A.heap-size - 1;
    MIN-HEAPIFY (A, 1);
    return min;

HEAP-DECREASE-KEY 伪代码如下:

HEAP-DECREASE-KEY (A, i, key):    //将结点i的key值下降到key的值并调整至正确位置
    if key > A[i]:
        error "新的key值大于当前key值"
    
    A[i] = key;
    while i > 1 and A[parent(i)] > A[i]:
        exchange A[parent(i)] and A[i];
        i = parent(i);

MIN-HEAP-INSERT  伪代码如下:

MIN-HEAP-INSERT(A, key):
    A.heap-size = A.heap-size + 1;
    A[A.heap-size] = 正无穷;
    HEAP-DECREASE-KEY (A, A.heap-size, key);

 

6.5-4:

因为HEAP-INCREASE-KEY 要求整个堆是最大堆, 如果在 A.heap-size 处插入负无穷,整个堆仍然是最大堆, 这样才能用 HEAP-INCREASE-KEY

 

6.5-5:

假定在调用 HEAP-INCREASE-KEY 方法时, 堆 A[1, A.heap-size] 是一个最大堆

初始化:如果新插入的key小于当前的A[i] 那么将无法插入key值, 此时的数组A[i, A.heap-size] 没有变化,仍然是一个最大堆, 反之, 新插入的key值大于当前的 A[i] , 那么除了A[i] 和 A[parent(i)] 之外, 其他的结点仍然满足最大堆, 如果此时A[i] > A[parent(i)]  那么, 此时不满足最大堆的性质, 但是因为之前A[parent(i)] > A[i] ,A[i] 大于A[i] 的子结点, 所以交换之后 A[i] 满足了最大堆的性质

保持:如果当前的A[i] 满足了最大堆的性质, 那么让i = parent(i) , 此时对于现在的 A[i] 仍然需要比较父结点的值, 同前面, 此交换 A[i] 和 A[parent(i)] 之后, 此时的i满足了最大堆的性质, 执行  i = parent(i)  之后为下次循环奠定了基础

结束:当结束时, 要么A[parent(i)] > A[i] 此时满足了最大堆的性质, 要么此时 i = 1, A[i] 为根节点,为整个堆中最大的元素, 此时整个堆仍然满足最大堆的性质 

 

 

6.5-6:

while (parent(i)>=1 and key > A[parent(i)]):
    A[i] = A[parent(i)];
    i = parent(i);

A[i] = key;

 

6.5-7:

 可以维护一个counter, 初始化为一个normal值, 然后每次执行依次 MAX-HEAP-INSERT 之前就让counter-1 作为key值给新插入的元素来实现一个队列, 反之, 在调用 MAX-HEAP-INSERTION 之前让 counter+1 作为新的key值给新插入的元素, 来实现栈的操作

 

6.5-8:

伪代码如下:

MAX-HEAP-DELETE(A, i):
    if i < 1: 
        error "堆下溢"
    exchange A[i] and A[A.heap-size];  //让A[i] 和当前堆的最后一个元素交换
    A.heap-size = A.heap-size - 1;        //将最后一个元素移除堆
    MAX-HEAPIFY(A, i);        //维持新的A[i] 的最大堆的性质, 将A[i]移动到适当的位置

 

6.5-9:

首先对于k个有序链表, 取出这k个有序链表中的最小的元素, 构建成最小堆, 此时时间复杂度是 O(k), 对于链表中的每一个结点, 需要维护一个当前所在列表的引用

然后取出最小堆的堆顶元素, 作为整个结果的最小元素, 然后取出刚才在堆顶的那个链表的下一个元素, 调整堆为最小堆, 然后再次取出堆顶元素, 作为次小的元素, 如果有一个队列空了, 就删除这个结点。因为每次堆的大小都是k, 所以每次调整为最小堆的时间都是 lgk , 所以总共有n结点, 每次调整堆的时间为 O(k), 所以总共的时间为   O(nlgk)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值