优先队列可以说是堆的一个非常重要的应用,和堆对应,优先队列也分最小优先队列和最大优先队列。
优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中每一个元素都有一个关键字(key),关键字赋予了一个元素的优先级,故名为优先队列。之所以用堆来实现优先队列,我想最大的原因是堆很容易对元素按关键字进行排序。
优先队列的应用:
最大优先队列:其中最为典型的就是“共享计算机系统的作业调度”,通过记录各个作业的优先级,来调度一个作业的执行、删除和插入等操作。
最小优先队列:可以被用于“基于事件驱动的模拟器”,队列中保存要模拟的事件,每个事件都有一个发生时间作为关键字。事件必须按发生的时间顺序进行模拟,因为某一事件的模拟结果可能会触发对其他事件的模拟。
优先队列的实现:
为了实现优先队列,需要实现这样的几个过程:
1)Insert(S, x):插入x到集合S中;
2)Maximum(S):返回S中具有最大关键字的元素;
3)Extract_Max(S):去掉并返回集合S中具有最大关键字的元素;
4)Increase_Key(S, x, key):将元素x的关键字增加到key。
我们暂且不用管这些奇怪的函数为什么要这么定义,因为这是前人的成功经验总结,肯定是在实际应用中这几个函数用得是最多的,总之,知道这样的四个函数就行了,等用到的时候就知道它们的好处了。
首先,基于堆,Maximum可以在O(1)的时间内完成,如:
1 int PriorityQueue::HeapMaximum() //return the maximum key from priority queue; 2 { 3 return GetQueueElement(0); //the first element is max; 4 }
其次,Extract_Max(S)和Maximum()的区别就在于,得到了之后还要删除,在不改变结构的情况下,最好的删除方法就是用最后一个元素来代替第一个元素,但是要注意,删除之后,有可能堆的性质就不能保证了,所以,这个时候调用Max_Heapify()调整一下。如下:
1 int PriorityQueue::HeapExtractMax() //delete and return the maximum key from queue; 2 { 3 if (IsEmptyHeap()) 4 throw "heap underflow"; 5 6 int heap_size = GetHeapSize(); 7 int maxNum = GetQueueElement(0); 8 SetQueueElement(0, GetQueueElement(heap_size-1)); //A[1] = A[heap_size] 9 SetHeapSizeMinus1(); 10 MaxHeapify(0); //Max_Heapify() 11 return maxNum; 12 }
对于Increase_Key(S, x, key),我们直接用Key替换X,但是替换之后堆的性质也可能改变了,可以知道的是,X后面的元素仍然满足堆的性质,因为Key>X,这时就之用维护X前面的元素,一层层往上调用Max_Heapify()即可,如:
1 void PriorityQueue::HeapIncreaseKey(int srcIndex, int dstKey) //increasing the srcKey to dstKey 2 { 3 if (dstKey < GetQueueElement(srcIndex)) 4 throw "new key is smaller than current key!"; 5 SetQueueElement(srcIndex, dstKey); //x = key 6 while(srcIndex > 0 && GetQueueElement(getParent(srcIndex)) < GetQueueElement(srcIndex)) { 7 Swap(srcIndex, getParent(srcIndex)); 8 srcIndex = getParent(srcIndex); //get parent 9 } 10 }
最后,Insert(S, x)操作,我们可以在最后增加一个-∞的元素,然后将其转换成将-∞增加到x,即转换成Increase_Key(S, -∞, x)。如:
1 void PriorityQueue::HeapInsert(int key) //insert key to the priority queue; 2 { 3 AddQueueElement(INT_MIN); //add the INT_MIN to the end of heapQueue; 4 int heap_size = GetHeapSize(); 5 HeapIncreaseKey(heap_size-1, key); 6 }
除此之外,习题6.5-8要求在O(lgn)的时间内实现一个Delete(S, i)操作。很容易想到相当于对A[i]为根的堆进行Extract_Max()操作。如:
1 Heap-Delete(A, i) 2 A[i] = A[A.heap-size] 3 A.heap-size = A.heap-size - 1 4 Heapify(A, i)
以上所有的操作,除了Maximum()之外,其余所有的函数的时间复杂度皆为O(lgn),所以,这也是为什么用堆来实现优先队列一个非常重要的原因。下面给出一个非常简单的优先队列的实现(元素的值就是Key)。
1 //PriorityQueue.h 2 3 4 #ifndef _PRIORITY_QUEUE_H_ 5 #define _PRIORITY_QUEUE_H_ 6 7 /************************************************************************/ 8 /* priority queue 9 /************************************************************************/ 10 class PriorityQueue { 11 public: 12 PriorityQueue() { m_heapSize = 0; m_length = 0; } 13 ~PriorityQueue() {} 14 15 //inline 16 int getParent(int index) { return (index-1)/2; } 17 int getLeft(int index) { return 2*index + 1; } 18 int getRight(int index) { return 2*index + 2; } 19 20 //heap operation 21 void BuildMaxHeap(); //build the max heap 22 void MaxHeapify(int index); //protect the max heap 23 void HeapSort(); //heap sort 24 25 //priority queue operation 26 void HeapInsert(int key); //insert key to the priority queue; 27 int HeapMaximum(); //return the maximum key from priority queue; 28 int HeapExtractMax(); //delete and return the maximum key from queue; 29 void HeapIncreaseKey(int srcIndex, int dstKey); //increasing the srcKey to dstKey 30 31 void HeapDelete(int key); //delete key 习题6.5-8 32 33 //other assist functions 34 void AddQueueElement(int key); 35 void DisplayQueue(); 36 void DisplayHeapQueue(); 37 38 public: 39 40 41 private: 42 int GetQueueElement(int index) { return m_vecQueue[index]; } 43 void SetQueueElement(int index, int key) { m_vecQueue[index] = key; } 44 void Swap(int i, int j) { 45 int temp = m_vecQueue[i]; 46 m_vecQueue[i] = m_vecQueue[j]; 47 m_vecQueue[j] = temp; 48 } 49 int GetHeapSize() { return m_heapSize; } 50 int GetArrayLength() { return m_length; } 51 void SetHeapSizeMinus1() { m_heapSize = m_heapSize - 1; } 52 void SetHeapSizePlus1() { m_heapSize = m_heapSize + 1; } 53 54 bool IsEmptyHeap() { return (m_heapSize > 1 ? false : true); } 55 56 private: 57 vector<int> m_vecQueue; 58 int m_heapSize; //堆元素个数 59 int m_length; //数组元素个数 60 61 }; 62 63 64 #endif//_PRIORITY_QUEUE_H_
1 //PriorityQueue.cpp 2 3 4 #include5 #include 6 using namespace std; 7 8 #include "PriorityQueue.h" 9 10 //heap operation 11 void PriorityQueue::BuildMaxHeap() //build the max heap 12 { 13 int heap_size = GetHeapSize(); 14 for (int i = (heap_size-1)/2; i >= 0; i --) 15 MaxHeapify(i); 16 } 17 18 void PriorityQueue::MaxHeapify(int index) //protect the max heap 19 { 20 if (index < 0 || index >= GetHeapSize()) 21 return; 22 23 int heap_size = GetHeapSize(); 24 bool isHeapify = true; 25 26 int largest = -1; 27 while (isHeapify && index < heap_size) { 28 int left = getLeft(index); 29 int right = getRight(index); 30 31 if (left < heap_size && GetQueueElement(left) > GetQueueElement(index) ) 32 largest = left; 33 else 34 largest = index; 35 if (right < heap_size && GetQueueElement(right) > GetQueueElement(largest) ) 36 largest = right; 37 38 if (largest != index) { 39 Swap(index, largest); 40 index = largest; 41 } 42 else 43 isHeapify = false; 44 } 45 46 } 47 48 void PriorityQueue::HeapSort() //heap sort 49 { 50 BuildMaxHeap(); 51 int heap_size = GetHeapSize(); 52 for (int i = heap_size-1; i >= 1; i --) { 53 Swap(0, i); 54 SetHeapSizeMinus1(); //heap_size--; 55 MaxHeapify(0); 56 } 57 } 58 59 //priority queue operation 60 void PriorityQueue::HeapInsert(int key) //insert key to the priority queue; 61 { 62 // SetHeapSizePlus1(); 63 // int heap_size = GetHeapSize(); 64 AddQueueElement(INT_MIN); //add the INT_MIN to the end of heapQueue; 65 int heap_size = GetHeapSize(); 66 HeapIncreaseKey(heap_size-1, key); 67 } 68 69 int PriorityQueue::HeapMaximum() //return the maximum key from priority queue; 70 { 71 return GetQueueElement(0); //the first element is max; 72 } 73 74 int PriorityQueue::HeapExtractMax() //delete and return the maximum key from queue; 75 { 76 if (IsEmptyHeap()) 77 throw "heap underflow"; 78 79 int heap_size = GetHeapSize(); 80 int maxNum = GetQueueElement(0); 81 SetQueueElement(0, GetQueueElement(heap_size-1)); //A[1] = A[heap_size] 82 SetHeapSizeMinus1(); 83 MaxHeapify(0); //Max_Heapify() 84 return maxNum; 85 } 86 87 void PriorityQueue::HeapIncreaseKey(int srcIndex, int dstKey) //increasing the srcKey to dstKey 88 { 89 if (dstKey < GetQueueElement(srcIndex)) 90 throw "new key is smaller than current key!"; 91 SetQueueElement(srcIndex, dstKey); //x = key 92 while(srcIndex > 0 && GetQueueElement(getParent(srcIndex)) < GetQueueElement(srcIndex)) { 93 Swap(srcIndex, getParent(srcIndex)); 94 srcIndex = getParent(srcIndex); //get parent 95 } 96 } 97 98 void PriorityQueue::HeapDelete(int key) //delete key 习题6.5-8 99 { 100 int heap_size = GetHeapSize(); 101 int index = 0; 102 for (;index < heap_size; index ++) { 103 if (GetQueueElement(index) == key) 104 break; 105 } 106 SetQueueElement(index, GetQueueElement(heap_size-1)); 107 SetHeapSizeMinus1(); 108 MaxHeapify(index); 109 } 110 111 //other assist functions 112 void PriorityQueue::DisplayQueue() 113 { 114 int nLen = GetArrayLength(); 115 cout << "------------------------" << endl; 116 for (int i = 0; i < nLen; i ++) 117 cout << GetQueueElement(i) << " "; 118 cout << endl; 119 } 120 121 void PriorityQueue::DisplayHeapQueue() 122 { 123 int heap_size = GetHeapSize(); 124 cout << "------------------------" << endl; 125 for (int i = 0; i < heap_size; i ++) 126 cout << GetQueueElement(i) << " "; 127 cout << endl; 128 } 129 130 void PriorityQueue::AddQueueElement(int key) 131 { 132 m_vecQueue.push_back(key); 133 m_heapSize ++; 134 m_length ++; 135 } 136 137 138 // int main() 139 // { 140 // //int key, num; 141 // PriorityQueue PQ; 142 // 143 // // cout << "the number:" << endl; 144 // // cin >> num; 145 // int arr[] = {10,8,7,16,14,9,3,2,4,1}; 146 // cout << "the key:" << endl; 147 // for (int i = 0; i < 10; i ++) { 148 // PQ.AddQueueElement(arr[i]); 149 // } 150 // 151 // PQ.BuildMaxHeap(); 152 // PQ.DisplayQueue(); 153 // 154 // //Max 155 // cout << "Max:" << PQ.HeapMaximum() << endl; 156 // 157 // //IncreaseKey 158 // cout << "IncreaseKey:" << endl; 159 // PQ.HeapIncreaseKey(0, 18); 160 // PQ.DisplayHeapQueue(); 161 // 162 // //InsertKey 163 // cout << "InsertKey:" << endl; 164 // PQ.HeapInsert(20); 165 // PQ.DisplayHeapQueue(); 166 // 167 // //Extract_Max 168 // cout << "Extract_Max:" << PQ.HeapExtractMax() << endl; 169 // PQ.DisplayHeapQueue(); 170 // 171 // //Extract_Max 172 // cout << "Extract_Max:" << PQ.HeapExtractMax() << endl; 173 // PQ.DisplayHeapQueue(); 174 // 175 // return 0; 176 // }
习题精讲:
优先队列的讲述,基本就是这样,其实主要的设计思想还是堆。下面看两道有意思的习题:
1)习题6.5-6:在Heap_Increase_Key的第5行操作中,一般需要通过三次赋值来完成。想一想如何利用Insertion_Sort内循环部分的思想,只用一次赋值就完成这一交换操作?
分析:这是一种非常好的出题思路,能够打开我们的思维,让人有一种眼前一亮的感觉。我们可以先向下移动小于他的祖先,直到没有小于他的祖先后放在空出的位置上。如:
1 Heap-Increase-Key(A, i, key) 2 if A[i] < key 3 error "new key is smaller than original" 4 while i > 1 and A[Parent(i)] < key 5 A[i] = A[Parent(i)] 6 i = Parent(i) 7 A[i] = key
2)习题6.5-9:请设计一个能够在O(nlgk)的算法,它能够将k个有序链表合并成一个有序链表,这里n是所有输入链表包含的总的元素个数。(提示:使用最小堆来完成k路归并)。
这个题首先想到2路归并排序,但此处是k路,乍一看没什么思路,看了提示后,仍然没有什么头绪,看了网友Anker的思路后,发现可以这样来做(太水了):创建一个大小为k的数组,将k个链表中的第一个元素依次存放到数组中,然后将数组调整为最小堆,这样保证数组的第一个元素是最小的,假设为min,将min从最小堆取出并存放到最终结果的链表中,此时将min所在链表的下一个元素到插入的最小堆中,继续上面的操作,直到堆中没有元素为止。举个例子如下图所示(只给出不部分操作):
我们采用C++语言,借助STL实现此过程,链表采用vector,最小堆中存放的是vector的迭代器,表示vector中元素的位置。完整程序如下:
1 //MinHeap_KMerge.cpp 2 3 #include4 #include 5 6 using namespace std; 7 8 #include "MinHeap.h" 9 10 //merge the k list to a heap; 11 template<class T> 12 MinHeap ::MinHeap(size_t kSize) 13 { 14 if (!m_minHeap) 15 delete []m_minHeap; 16 m_minHeap = new T[kSize+1]; 17 m_heapSize = 0; 18 } 19 20 //adjust the min heap; 21 template<class T> 22 void MinHeap ::MinHeapify(size_t index) 23 { 24 //assert 25 size_t heap_size = GetHeapSize(); 26 27 while (true) { 28 size_t left = LEFT(index); 29 size_t right = RIGHT(index); 30 31 size_t smallest; 32 if (left < heap_size && HeapCompare(left, index)) 33 smallest = left; 34 else smallest = index; 35 if (right < heap_size && HeapCompare(right, smallest)) 36 smallest = right; 37 38 if (smallest != index) { 39 Swap(index, smallest); 40 index = smallest; 41 } 42 else break; 43 } 44 } 45 46 //insert element 47 template<class T> 48 void MinHeap ::HeapInsert(const T &element) 49 { 50 m_minHeap[m_heapSize] = element; 51 m_heapSize += 1; 52 53 size_t index = m_heapSize-1; 54 55 while (index > 0 && HeapCompare(index, PARENT(index))) { 56 Swap(index, PARENT(index)); 57 index = PARENT(index); 58 } 59 } 60 61 //return and delete the min element 62 template<class T> 63 T MinHeap ::HeapExtractMin() 64 { 65 if (IsEmptyHeap()) 66 throw "Heap is Empty!"; 67 T minElement = HeapMin(); 68 69 int heap_size = GetHeapSize(); 70 m_minHeap[0] = m_minHeap[heap_size-1]; 71 m_heapSize -= 1; 72 MinHeapify(0); 73 return minElement; 74 } 75 76 //return min element; 77 template<class T> 78 T MinHeap ::HeapMin() const 79 { 80 return m_minHeap[0]; 81 } 82 83 int main() 84 { 85 const size_t k = 3; 86 vector<int> vecList[k]; 87 vector<int>::iterator iterList[k]; 88 vector<int> vecSort; 89 vector<int>::iterator iterS; 90 91 vector<int>::iterator it; 92 93 MinHeap int>::iterator> minHeap(k); 94 //first list 95 vecList[0].push_back(12); 96 vecList[0].push_back(24); 97 vecList[0].push_back(52); 98 cout << "first list:" << endl; 99 for ( it = vecList[0].begin();it != vecList[0].end(); ++it) 100 cout << *it << "->"; 101 cout << "NULL" << endl; 102 103 vecList[1].push_back(9); 104 vecList[1].push_back(32); 105 106 cout << "second list:" << endl; 107 for ( it = vecList[1].begin();it != vecList[1].end(); ++it) 108 cout << *it << "->"; 109 cout << "NULL" << endl; 110 111 vecList[2].push_back(34); 112 vecList[2].push_back(42); 113 vecList[2].push_back(78); 114 cout << "third list:" << endl; 115 for ( it = vecList[2].begin();it != vecList[2].end(); ++it) 116 cout << *it << "->"; 117 cout << "NULL" << endl; 118 119 iterList[0] = vecList[0].begin(); 120 iterList[1] = vecList[1].begin(); 121 iterList[2] = vecList[2].begin(); 122 123 124 minHeap.HeapInsert(iterList[0]); 125 minHeap.HeapInsert(iterList[1]); 126 minHeap.HeapInsert(iterList[2]); 127 128 while(minHeap.GetHeapSize()) { 129 it = minHeap.HeapExtractMin(); 130 vecSort.push_back(*it); 131 ++it; 132 if (it != vecList[0].end() && it != vecList[1].end() && it != vecList[2].end()) { //!!!!Expression vector iterators incompatible 133 minHeap.HeapInsert(it); 134 } 135 } 136 137 cout << "merge:" << endl; 138 for (iterS = vecSort.begin(); iterS != vecSort.end(); ++ iterS) 139 cout << *iterS << "->"; 140 cout << "NULL" << endl; 141 142 return 0; 143 }
开了个公众号「aCloudDeveloper」,专注技术干货分享,期待与你相遇。