算导本章的优先级队列是堆数据结构的一个应用,优先级队列分为两种:最大优先级队列和最小优先级队列。主要内容是基于最大堆实现最大优先级队列。
关于堆数据结构的内容见此:堆。下文有关堆的代码解释也在链接的文章。
“优先级队列是一种用来维护由一种元素构成的集合S的数据结构,这一组元素中的每一个都有一个关键字key。“------《算法导论》
我的代码实现与算导中关于最大优先级队列的支持操作有出入,我将increase-key操作删除,把insert操作修改了一下。
具体支持的操作为下:
1 int heap_maximum(int A[]); //返回优先队列的最大值 2 3 int heap_extract_max(int A[], int length); //删除并返回最大值 4 5 int *max_heap_insert(int A[], int length, int key); //插入值为key的元素进优先队列中
返回优先级队列的最大值
这个操作的运行时间为Θ(1), 就是简单的返回而已。
1 int heap_maximum(int A[]){ 2 return A[1]; 3 }
去掉并返回S中具有最大关键字的元素
这个操作的运行时间为O(lgn),主要在去掉最大关键字的元素后要保证最大堆的性质,则调用max_heapify函数。
1 /* 2 * 先记录最大值,将数组最后的元素与第一个元素交换,数组大小减一, 3 * 调用max_heapify函数保证最大堆的性质,最后返回最大值。 4 */ 5 int heap_extract_max(int A[], int length){ 6 int max = A[1]; 7 A[1] = A[length]; 8 length--; 9 max_heapify(A, length, 1); 10 return max; 11 }
将元素插入集合S
算导是通过插入一个无穷小的元素到集合里,再将这个元素增加到key值的方法插入,详见书本内容。我的方法是直接插入,所以没有了将某个元素的值增加到某值的操作。
但是插入后的操作是一样,维持最大堆的性质。
在本节点往根节点移动的路上寻找该元素合适的位置,该元素不断地与其父节点相比,如果该元素的关键字比父节点的关键字大就交换它们的位置并继续移动。
运行时间为O(lgn)因为从新插入的节点到根节点的路径为O(lgn).
1 /* 2 * 先用一个指针指向数组A,再重新分配数组A的大小,将原来的数据复制到新的数组A里, 3 * 并将key值放入数组A,为了保持最大堆的性质,将插入的值与其父节点的值进行比较, 4 * 如果此节点的值大,就与父节点交换,知道此节点的值小于其父节点。 5 * 无需调用max_heapify函数,通过这种上移的方法不影响原最大堆的性质。 6 */ 7 int *max_heap_insert(int A[], int length, int key){ 8 int i; 9 int *B = A; 10 A = malloc( (length + 2) * sizeof(int)); 11 12 for(i = 1; i <= length; i++) 13 A[i] = B[i]; 14 A[length + 1] = key; 15 16 printf("Test:\n"); 17 for(i = 1; i <= length+1; i++) 18 printf("%d ", A[i]); 19 printf("\n"); 20 21 i = length + 1; 22 while(i > 1 && A[i/2] < A[i]){ 23 int temp = A[i/2]; 24 A[i/2] = A[i]; 25 A[i] = temp; 26 27 i = i / 2; 28 } 29 return A; 30 }
对于优先级队列的具体应用,其元素对应着应用中的对象。因此用堆来实现优先级队列时,需要在堆中的每个元素里存储对应的应用对象的柄。
具体的对象柄可以是(指针、整数、结构体等)。下文的练习就是通过优先级队列实现先进先出的队列和栈。
先进先出队列
用最小优先级队列实现(本节就有条用最小堆实现最小优先级队列的练习,因为这个队列用最小优先级队列实现,所以就只做这个练习,而且最小优先级队列的实现
与最大优先级队列的实现只是有少许不一样),具体思路为:标记每个元素进入队列的顺序(结构体实现),因为先进先出的队列的特性为最先进入队列的元素在出队列时
最先。通过对元素添加一个属性来记录顺序,再建立最小堆(比较的属性为顺序),这样最小优先级队列建立成功。出队列对应去掉并返回顺序为最小的元素。
PS:因为是在前文的代码上加以修改,所以没写太多注释,注释可以参考前文
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 struct queue_node { 5 int num; //记录元素的顺序 6 int value; //元素的关键值 7 }; 8 9 typedef struct queue_node queue_node; 10 11 void min_heapify(queue_node A[], int length, int i); 12 13 void build_min_heap(queue_node A[], int length); 14 15 int heap_firstout(queue_node A[]); 16 17 int heap_extract_min(queue_node A[], int length); 18 19 queue_node *min_heap_insert(queue_node A[], int length, int num, int key); 20 21 int main(){ 22 int num, No; 23 printf("Input the number:\n"); 24 scanf("%d", &num); 25 26 queue_node *array = malloc((num+1) * sizeof(queue_node)); 27 28 printf("Input the element:\n"); 29 int i; 30 for(i = 1, No = 1; i <= num; i++, No++){ 31 array[i].num = No; 32 scanf("%d", &array[i].value); 33 } 34 35 printf("Build the queue!\n"); 36 build_min_heap(array, num); 37 printf("The result:\n"); 38 for(i = 1; i <= num; i++) 39 printf("No.%d: %d ", array[i].num, array[i].value); 40 printf("\n"); 41 42 int insert_num; 43 printf("Input the element you want to insert into the heap:\n"); 44 scanf("%d", &insert_num); 45 array = min_heap_insert(array, num, No, insert_num); 46 num++; 47 No++; 48 49 printf("Output the queue:\n"); 50 int first_out; 51 while(num >= 1){ 52 first_out = heap_extract_min(array, num); 53 num--; 54 printf("%d ", first_out); 55 } 56 printf("\n"); 57 58 return 0; 59 } 60 61 void min_heapify(queue_node A[], int length, int i){ 62 int l, r; 63 int smallest, t_num, t_value; 64 65 while(i <= length){ 66 l = 2 * i; 67 r = 2 * i + 1; 68 69 if(l <= length && A[l].num < A[i].num) 70 smallest = l; 71 else 72 smallest = i; 73 if(r <= length && A[r].num < A[smallest].num) 74 smallest = r; 75 76 if(smallest != i){ 77 t_num = A[smallest].num; 78 t_value = A[smallest].value; 79 A[smallest].num = A[i].num; 80 A[smallest].value = A[i].value; 81 A[i].num = t_num; 82 A[i].value = t_value; 83 i = smallest; 84 } 85 else 86 break; 87 } 88 } 89 90 void build_min_heap(queue_node A[], int length){ 91 int i; 92 for(i = length/2; i >= 1; i--) 93 min_heapify(A, length, i); 94 } 95 96 /*int heap_firstout(queue_node A[]){ 97 return A[1].value; 98 }*/ 99 100 int heap_extract_min(queue_node A[], int length){ 101 int first_out = A[1].value; 102 A[1].num = A[length].num; 103 A[1].value = A[length].value; 104 length--; 105 min_heapify(A, length, 1); 106 return first_out; 107 } 108 109 queue_node *min_heap_insert(queue_node A[], int length, int num, int key){ 110 int i; 111 queue_node *B = A; 112 A = malloc((length + 2) * sizeof(queue_node)); 113 114 for(i = 1; i <= length; i++){ 115 A[i].num = B[i].num; 116 A[i].value = B[i].value; 117 } 118 A[length + 1].num = num; 119 A[length + 1].value = key; 120 121 printf("Test:\n"); 122 for(i = 1; i <= length+1; i++) 123 printf("No.%d: %d ", A[i].num, A[i].value); 124 printf("\n"); 125 126 i = length + 1; 127 while(i > 1 && A[i/2].num > A[i].num){ 128 int t_num = A[i].num; 129 int t_value = A[i].value; 130 A[i].num = A[i/2].num; 131 A[i].value = A[i/2].value; 132 A[i/2].num = t_num; 133 A[i/2].value = t_value; 134 135 i = i / 2; 136 } 137 return A; 138 }