zoukankan      html  css  js  c++  java
  • 算法导论第六章优先队列(二)

    优先队列可以说是堆的一个非常重要的应用,和堆对应,优先队列也分最小优先队列和最大优先队列。

    优先队列是一种用来维护由一组元素构成的集合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_
    View Code
      1 //PriorityQueue.cpp
      2 
      3 
      4 #include <iostream>
      5 #include <vector>
      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 // }
    View Code

    习题精讲:

    优先队列的讲述,基本就是这样,其实主要的设计思想还是堆。下面看两道有意思的习题:

    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 #include <iostream>
      4 #include <vector>
      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<T>::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<T>::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<T>::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<T>::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<T>::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<vector<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 }
    View Code

    我的公众号 「Linux云计算网络」(id: cloud_dev),号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

  • 相关阅读:
    asp.net 启动关闭iis
    vue 界面关闭触发事件 ---实例销毁之前调用
    ElmentUI 设置禁止点击遮罩关闭 el-dialog 弹窗
    C#反射
    SQL Server 创建游标(cursor)
    文件解压缩
    文件流操作
    Linq查询
    C#线程 多线程 进程
    匿名类型和反射
  • 原文地址:https://www.cnblogs.com/bakari/p/4823753.html
Copyright © 2011-2022 走看看