【笔记】
一个最大优先队列支持以下操作:
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)