《算法导论》笔记 第6章 6.5优先级队列

【笔记】

一个最大优先队列支持以下操作:

insert x:把元素x插入集合。

maximum:返回集合中具有最大关键字的元素。

extract-max:去掉并返回集合中具有最大关键字的元素。

increase-key x k:将元素x的关键字增加到k,这里k值不能小于x的原关键字的值。

    Type heapMaximum() {
        return A[1];
    }
    Type heapExtractMax() {
        if (heapSize < 1) return 0;
        int max = A[1];
        A[1] = A[heapSize--];
        maxHeapify(1);
        return max;
    }
    void heapIncreaseKey(int i,Type key) {
        if (key < A[i]) return;
        A[i] = key;
        while (i>1 && A[parent(i)] < A[i]) {
            swap(A[i],A[parent(i)]);
            i = parent(i);
        }
    }
    void maxHeapInsert(Type key) {
        heapSize++;
        A[heapSize] = key;
        heapIncreaseKey(heapSize,key);
    }

【练习】

6.5-1 描述对堆 A = <15,13,9,5,12,8,7,4,0,6,2,1> 执行 HEAP_EXTRACT_MAX 操作的过程。

记录返回值为15,将最后一个元素移动到根结点,堆变成 A = <1,13,9,5,12,8,7,4,0,6,2>,

对根结点执行MAX_HEAPIFY,最终堆为 A = <13,12,9,5,6,8,7,4,0,1,2>





6.5-2 描述在堆 A = <15,13,9,5,12,8,7,4,0,6,2,1> 上执行 MAX_HEAP_INSERT(A,10) 操作的过程。




6.5-3 使用最小堆实现最小优先队列写出HEAP_MINIMUM,HEAP_EXTRACT_MIN,HEAP_DECREASE_KEY和MIN_HEAP_INSERT过程。

    Type heapMinimum() {
        return A[1];
    }
    Type heapExtractMin() {
        if (heapSize < 1) return 0;
        int min = A[1];
        A[1] = A[heapSize--];
        minHeapify(1);
        return min;
    }
    void heapDecreaseKey(int i,Type key) {
        if (key > A[i]) return;
        A[i] = key;
        while (i>1 && A[parent(i)] > A[i]) {
            swap(A[i],A[parent(i)]);
            i = parent(i);
        }
    }
    void minHeapInsert(Type key) {
        heapSize++;
        A[heapSize] = key;
        heapDecreaseKey(heapSize,key);
    }

6.5-4 为什么我们在 MAX_HEAP_INSERT 先将关键字的值设置为无穷小,而后又将此关键值增大到所要求的值呢?

不知道呀,直接设成所要求的值不行吗不行吗?> <


6.5-5 使用以下的循环不变式来论证 HEAP_INCREASE_KEY 的正确性:

在while循环的每次迭代之初,数组 A[1..heap-size[A]]满足最大堆性质,除了一个可能的例外:A[i]可能大于A[PARENT(i)]。

初始化:在第1轮迭代之前。设结点i当前的值为 A[i],之前的值为 A'[i],可知 A[i] >= A'[i]。

在结点i的值改变之前,数组A满足最大堆性质,因此A'[i] >= LEFT(i),A'[i] >= RIGHT(i)。

因此A[i]>=LEFT(i),A[i]>=RIGHT(i)。由于只有结点i的数值增大过,A[i]可能大于A[PARENT(i)]。

保持:若结点i大于父结点,则交换父结点与结点i,由最大堆的性质可知:A[PARENT(i)]>=A'[i]>=LEFT(i) and RIGHT(i)。

因此A[PARENT(i)] >= A[LEFT(i)] and A[RIGHT(i)]且 A[PARENT(i)] <= A[i],A[i]>=A[PARENT(i)]>=A'[LEFT(PARENT(i))] and A'[RIGHT(PARENT(i))]。

交换PARENT(i)与i可使最大堆的性质得到保持,除了一个可能的例外:A[i]可能大于新的父结点A[PARENT(i)]。为下一次迭代重新建立了循环不变式。

终止:过程终止时,i为根结点或A[i]<=A[PARENT(i)]。此时数组满足最大堆性质,并且没有例外。


6.5-6 说明如何使用优先级队列来实现一个先进先出队列,另请说明如何用优先级队列来实现栈。

对插入的元素赋一个权值,优先队列通过比较权值来维护最大堆。

队列:第一个插入的元素拥有最大的权值,随后插入的元素权值依次递减。此时,元素先进先出。

栈:第一个插入的元素拥有最小的权值,随后插入的元素权值依次递增。此时,元素先进后出。


6.5-7 HEAP-DELETE(A,i) 操作将节点i中的项从堆A中删去。对含n个元素的最大堆,请给出时间为O(logn)的HEAP_DELETE的实现。

    void heapDeleteMax(int i) {
        if (i<1||i>heapSize) return;
        A[i] = A[heapSize--];
        maxHeapify(i);
    }

6.5-8 请给出一个时间为O(nlogk)、用来将k个已排序链表合并为一个排序链表的算法。此处n为所有输入链表中元素的总数。

1、将k个链表的第一个元素插入堆中。

2、取出堆中的最小元素,若该元素所在的链表不为空,将其下一个元素插入到堆中。重复这一步操作直到取出堆中所有元素。

复杂度证明:

优先队列中的元素总数不会超过k,插入一个元素的复杂度为O(logk),弹出队首元素复杂度为O(logk)。

n个元素各入队出队一次,因此总复杂度为O(n)*(O(logk)+O(logk)) = O(nlogk)




原文地址:https://www.cnblogs.com/cyendra/p/3681629.html