《算法導論》讀書筆記之第6章 優先級隊列


1、概述

  隊列是一種滿足先進先出(FIFO)的數據結構,數據從隊列頭部取出,新的數據從隊列尾部插入,數據之間是平等的,不存在優先級的。這個就類似於普通老百姓到火車站排隊買票,先來的先買票,每個人之間是平等的,不存在優先的權利,整個過程是固定不變的。而優先級隊列可以理解為在隊列的基礎上給每個數據賦一個權值,代表數據的優先級。與隊列類似,優先級隊列也是從頭部取出數據,從尾部插入數據,但是這個過程根據數據的優先級而變化的,總是優先級高的先出來,所以不一定是先進先出的。這個過就類似於買火車票時候軍人比普通人優先買,雖然軍人來的晚,但是軍人的優先級比普通人高,總是能夠先買到票。通常優先級隊列用在操作系統中的多任務調度,任務優先級越高,任務優先執行(類似於出隊列),后來的任務如果優先級比以前的高,則需要調整該任務到合適的位置,以便於優先執行,整個過程總是使得隊列中的任務的第一任務的優先級最高。

  優先級隊列有兩種:最大優先級隊列和最小優先級隊列,這兩種類別分別可以用最大堆和最小堆實現。書中介紹了基於最大堆實現的最大優先級隊列。一個最大優先級隊列支持的操作如下操作:

INSERT(S,x):把元素x插入到集合S

MAXIMUM(S):返回S中具有最大關鍵字的元素

EXTRACT_MAX(S):去掉並返回S中的具有最大關鍵字的元素

INCREASE_KEY(S,x,k):將元素x的關鍵字的值增加到k,這里k值不能小於x的原關鍵字的值。

2、最大優先級隊列操作實現

  采用最大堆實現最大優先級隊列,關於最大堆可以參見上一篇日志http://www.cnblogs.com/Anker/archive/2013/01/23/2873422.html

(1)HEAP_MAXIMUM用O(1)時間實現MAXIMUM(S)操作,即返回最大堆第一個元素的值即可(return A[1])。

(2)HEAP_EXTRACT_MAX實現EXTRACT_MAX操作,刪除最大堆中第一個元素,然后調整堆。操作過程如下:將最堆中最后一個元素復制到第一個位置,刪除最后一個節點(將堆的大小減少1),然后從第一個節點位置開始調整堆,使得稱為新的最大堆。操作過程如下圖所示:

偽代碼描述如下:

1 HEAD_EXTRACT_MAX(A)
2  if heap_size[A]<1
3    ther error
4  max = A[1]
5  A[1] = A[heap_size[A]];
6  heap_size[A] = heap_size[A]-1
7  adjust_max_heap(A,1)
8  return MAX

(3)HEAP_INCREASE_KEY實現INCREASE_KEY,通過下標來標識要增加的元素的優先級key,增加元素后需要調整堆,從該節點的父節點開始自頂向上調整。操作過程如下圖所示:

偽代碼描述如下:

1 HEAP_INCREASE_KEY(A,i,key)
2    if key < A[i]
3      then error
4    A[i] = key
5   while i>1 && A[PARENT(i)] <A[i]
6        do exchange A[i] <-> A[PARENT(i)]
7        i = PARENT(i)

(4)MAX_HEAP_INSERT實現INSERT操作,向最大堆中插入新的關鍵字。新的關鍵字插入在優先級的隊尾部,然后從尾部的父節點開始自頂向上調整堆偽代碼描述如下:

1 MAX_HEAP_INSERT(A,key)
2   heap_size[A] = heap_size[A]+1
3   A[heap_size[A]] = -0;
4   HEAP_INCREASE_KEY(A,heap_size[A],key)

 3、實例

 問題描述如下:優先級隊列中有多個事件發生,每個事件有自己獨立的優先級,優先級是非負數,數值越大優先級越高。采用最大優先級隊列模擬事件執行的優先順序。具體操作包括:

(1)向優先級隊列中添加一個新事件

(2)獲取優先級隊列中優先級最高的事件

(3)刪除優先級隊列中指定位置的事件

(4)增加優先級隊列中指定位置事件的優先級

(5)降低優先級隊列中指定位置事件的優先級

采用C++語言實現,完整程序如下所示:

View Code
  1 #include <iostream>
  2 #include <string>
  3 #include <cstdlib>
  4 using namespace std;
  5 
  6 const static int QUEUELEN = 100;
  7 
  8 class Event
  9 {
 10 public:
 11     Event():eventname(""),priority(-1){};
 12     Event(const string &en,const int p):eventname(en),priority(p){};
 13     Event(const Event& en)
 14     {
 15         eventname = en.eventname;
 16         priority = en.priority;
 17     }
 18     ~Event(){};
 19     int get_event_priority()const
 20     {
 21         return priority;
 22     }
 23     string get_event_name()const
 24     {
 25         return eventname;
 26     }
 27     void increase_event_priority(const int k)
 28     {
 29         priority = priority + k;
 30     }
 31     void decrease_event_priority(const int k)
 32     {
 33          priority = priority - k;
 34     }
 35     void show_event() const
 36     {
 37         cout<<"Eventname is: ("<<eventname<<") and the priority is: "<<priority<<endl;
 38     }
 39 private:
 40     string eventname;
 41     int priority;
 42 };
 43 class PriorityQueue
 44 {
 45 public:
 46     PriorityQueue();
 47     void adjust_event(int index);
 48     Event get_event()const;
 49     void insert_event(const Event& en);
 50     void increase_event_priority(int pos,int k);
 51     Event delete_event(int pos);
 52     void show_events() const;
 53     ~PriorityQueue();
 54 private:
 55     Event *events;
 56     int length;
 57 };
 58 
 59 PriorityQueue::PriorityQueue()
 60 {
 61     events = new Event[QUEUELEN];
 62     length = 0;
 63 }
 64 
 65 PriorityQueue::~PriorityQueue()
 66 {
 67     if(!events)
 68         delete [] events;
 69     length = 0;
 70 }
 71 //adjust max heap 
 72 void PriorityQueue::adjust_event(int index)
 73 {
 74     int left,right,largest;
 75     Event temp;
 76     while(1)
 77     {
 78         left = index*2;
 79         right = index*2+1;
 80         if(left <= length &&
 81            events[left].get_event_priority() > events[index].get_event_priority())
 82             largest = left;
 83         else
 84             largest = index;
 85         if(right <= length &&
 86            events[right].get_event_priority() > events[largest].get_event_priority())
 87             largest = right;
 88         if(largest != index)
 89         {
 90             temp = events[index];
 91             events[index] = events[largest];
 92             events[largest] = temp;
 93             index = largest;
 94         }
 95         else
 96             break;
 97     }
 98 }
 99 Event PriorityQueue::get_event()const
100 {
101     if(length != 0)
102         return events[1];
103     else
104         return Event();
105 }
106 
107 void PriorityQueue::insert_event(const Event& en)
108 {
109     length = length + 1;
110     events[length] = en;
111     increase_event_priority(length,0);
112 }
113 
114 void PriorityQueue::increase_event_priority(int pos,int k)
115 {
116     int i,parent;
117     Event temp;
118     if(pos > length)
119     {
120         cout<<"error: the pos index is larger than queue length"<<endl;
121         return;
122     }
123     events[pos].increase_event_priority(k);
124     i = pos;
125     parent = i/2;
126     while(i>1
127           && events[parent].get_event_priority() < events[i].get_event_priority())
128     {
129         temp = events[i];
130         events[i] = events[parent];
131         events[parent] = temp;
132         i = parent;
133         parent = i/2;
134     }
135 }
136 
137 Event PriorityQueue::delete_event(int pos)
138 {
139     Event reten;
140     if(pos > length)
141     {
142         cout<<"Error:pos index is larger than queue length"<<endl;
143         return reten;
144     }
145     reten = events[pos];
146     events[pos] = events[length];
147     length--;
148     adjust_event(pos);
149     return reten;
150 }
151 void PriorityQueue::show_events() const
152 {
153     if(length == 0)
154     {
155         cout<<"There is no any event in the priority queue"<<endl;
156     }
157     else
158     {
159         cout<<"There are "<<length<<" events in the priority queue."<<endl;
160         for(int i=1;i<=length;i++)
161         {
162             events[i].show_event();
163         }
164     }
165 
166 }
167 int main()
168 {
169     PriorityQueue pqueue;
170     Event en;
171     Event en1("fork",2);
172     Event en2("exec",3);
173     Event en3("wait",1);
174     Event en4("signal",6);
175     Event en5("pthread_create",5);
176     pqueue.insert_event(en1);
177     pqueue.insert_event(en2);
178     pqueue.insert_event(en3);
179     pqueue.insert_event(en4);
180     pqueue.insert_event(en5);
181     pqueue.show_events();
182     cout<<"\nThe max priority event is: "<<endl;
183     en = pqueue.get_event();
184     en.show_event();
185     cout<<"\nIncrese event3 by 7"<<endl;
186     pqueue.increase_event_priority(3,7);
187     en = pqueue.get_event();
188     en.show_event();
189     pqueue.show_events();
190     cout<<"\nDelete the first event:"<<endl;
191     pqueue.delete_event(1);
192     pqueue.show_events();
193     exit(0);
194 }

程序測試結果如下所示:


4、問題

(1)如何使用優先級隊列實現一個先進先出的隊列和先進后出的棧?

  我的想法是:隊列中的元素是先進先出(FIFO)的,因此可以借助最小優先級隊列實現隊列。具體思想是,給隊列中的每個元素賦予一個權值,權值從第一個元素到最后一個依次遞增(如果采用數組實現的話,可以用元素所在的下標作為優先級,優先級小的先出隊列),元素出隊列操作每次取優先級隊列第一個元素,取完之后需要堆最小優先級隊列進行調整,使得第一個元素的優先級最小。棧中的元素與隊列剛好相反,元素是先進后出(FILO),因此可以采用最大優先級隊列進行實現,與用最小優先級隊列實現隊列思想類似,按照元素出現的順序進行標記元素的優先級,數據越是靠后,優先級越高。

  舉例說明采用最小優先級隊列實現先進先出隊列,現在有一組數A={24,15,27,5,43,87,34}共六個數,假設數組下標從1開始,以元素所在數組中的下標為優先級創建優先級隊列,隊列中元素出入時候調整最小優先級隊列。操作過程如下圖所示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM