堆與優先順序隊列

來源:互聯網
上載者:User

本文連結:堆與優先順序隊列

 

(優先順序隊列的完整代碼在最後)

 

堆,是一個很有意思的資料結構。邏輯結構是樹,一般為二叉樹,每個節點的值都大於(小於)其子樹中的任意節點。也就是說,使用堆結構的數組中,元素是部分有序的。而正是這個特點,使得在堆上,得到最大值(最小值)的時間複雜度為O(1),移除最大值(最小值)、插入元素、改變元素值(或者是刪除位元置已知的元素)的時間複雜度為O(lgn)。另外,用堆結構的排序是一種原地的、時間複雜度為O(nlgn)的排序演算法。

 

 

在優先順序隊列前先說一下堆排序。

堆排序和歸併排序都是時間複雜度為O(nlgn)的排序演算法,不同的是,歸併排序不是原地排序,它需要額外的n的空間;而堆排序是在原數組中進行的。

堆排序的過程,首先需要在給定的數組上進行建堆,然後在此基礎上一個一個的取根節點放到數組的後部,從後向前排。

建堆的過程BuildHeap代碼很簡單,從下到上對每個節點依次處理,處理中調用了另一個方法:Heapify,這個才是核心(具體參見完整代碼中的私人BuildHeap(void)方法與Heapify(ItemType
items[], int index, int count, bool isMaxHeap)方法);Heapify方法對指定節點的子樹進行堆化,具體地說,當除了根節點以外,其子樹中的所有節點都已經符合堆的要求時,Heapify方法會把指定的節點的子樹調整為一個堆,也就是把指定節點調整到子樹中合適的位置上,其時間複雜度為子樹的深度,O(lgn)。所以,可以算出建堆的時間複雜度為O(n),是線性。這個時間複雜度怎麼得到的?n長度的樹,有lgn層,第h層(從0開始)至多有2^h的節點,進行一下求和就行了。

堆建好了,下來就是排序(具體可以見完整代碼中私人的SortTo(ItemType destitionArray[])方法)。因為堆的根節點是最值,所以只需要依次把根節點和堆的最後一個元素交換位置,堆大小-1,再對根節點調用Heapify就可以了。時間複雜度也就是n個Heapify,O(nlgn)。

這樣從而得到總的排序時間複雜度O(nlgn)。

 

 

優先順序隊列,目的是從隊列中取出的一個元素總是當前隊列中的最大值(最小值)。而堆剛好具有這個特點。當然,有序的ArrayList或者LinkedList也是可以的,但是慢,這個後面分析。

看一下優先順序隊列都需要哪些操作。第一,其根本目的是能取出一個最值元素,這個就不用說了;第二,要能往隊列中插入元素。這是兩個核心的功能,另帶一些其他的協助工具功能,如改變其中的某個元素的值,刪除某個元素,得到其中的所有元素,得到其中元素個數等等。

 

給出優先順序隊列的類的介面:(完整的代碼見最後)

template<br /><typename ItemType><br />class PriorityQueue {<br />PriorityQueue(bool isMaxHeap = true);<br />PriorityQueue(int size, bool isMaxHeap = true);<br />PriorityQueue(ItemType items[], int count, bool isMaxHeap = true);<br />int Count(void);<br />ItemType Get();<br />ItemType Remove();<br />void Insert(ItemType value);<br />void Change(int index, ItemType value);<br />ItemType Remove(int index);<br />bool ToArray(ItemType destinationArray[], int destinationSize);<br />~PriorityQueue(void);<br />}<br />

 

 Remove()方法是得到並移除第一個元素,Get()僅僅是返回第一個元素。這個優先順序隊列用堆實現,從而可以得到主要方法Get()(O(1))、Remove()(O(lgn))、Insert()(O(lgn))較好的效能。具體實現參見完整代碼。

Heapify方法書上給的是個遞迴的,我給改成迭代的了,避免函數調用帶來的些許開銷,從而可以提高一點內隱的時間與空間的效能。

 

那麼為什麼用堆實現優先順序隊列比較合適呢?有序的線性表呢?

這個不難想,對於線性表,基於數組實現:首先,如果無序,要Get()、要Remove(),肯定要找到個最值,如果每次都去重新搜尋,就很慢了,而且數組牽扯到資料的整體移動(O(n));而保持有序,雖然Get()、Remove()快,但是Insert(ItemType
value)費的功夫有些多,因為堆中只是部分有序,而數組需要保持整體有序,又是資料整體移動(O(n))。

基於鏈表的呢?鏈表的修改是很快,但鏈表如何快速搜尋與定位?建索引?-_-!

 

 

扯回來,說些關於具體實現時的問題。

當然,Change(int index, ItemType value)與Remove(int index)兩個方法在實際中是不實用的,或者是不現實的。因為通常情況下,不知道某個元素的index是多少。通常要麼就不實現,要麼需要搜尋,或者有類似索引的機制。

還有,實際使用中隊列中存放的元素很可能是個什麼東西的指標,而不是一個沒什麼用的具體數值,這樣就還需要進行改進。

一種方式是原有的類不動,用一個實現了比較子的類或結構體的封裝類封裝一個有實際意義的東西,用這個類型當作PriorityQueue的模板參數。

或者是改造PriorityQueue,把模板參數ItemType改成一個結構體或類,思想和上面的相似,不過這樣做功能會強大許多。比如添加一個這樣的內部類來替代模板參數ItemType:

class QueueItem {<br />public:<br />QueueItem(ItemType theValue, int thePriority)<br />: priority(thePriority), value(theValue) {<br />}</p><p>// Getters and Setters</p><p>private:<br />friend PriorityQueue;<br />inline void SetOnwer(PriorityQueue* queue) {<br />onwer = queue;<br />}<br />inline void SetIndex(int index) {<br />this->index = index;<br />}<br />inline int GetIndex() {<br />return index;<br />}<br />private:<br />PriorityQueue* onwer;<br />int index;<br />int priority;<br />ItemType value;<br />};

然後把原先代碼中,items改為QueueItem類型的數組;類內部的的ItemType換成QueueItem的指標。QueueItem含有所在PriorityQueue的index,所有的地方注意改動這個;Insert方法中new一個QueueItem來封裝ItemType,並返回此類的指標供外部更改使用(構造器就沒辦法了,可能無法保留帶有元素的構造方法);移除的方法中注意回收QueueItem的空間;涉及到index的介面可以改為傳入QueueItem的指標,PriorityQueue可直接根據QueueItem指標指向的對象得到index;……細節比較瑣碎,不再描述,這個已經超出了本文的範圍。

 

 

完整的類代碼,cpp.net啟用託管支援的環境下測試的,不保證能在標準cpp環境下編譯通過(展開查看):

#include <exception><br />#pragma once</p><p>using std::exception;</p><p>template<br /><typename ItemType><br />class PriorityQueue {<br />private:<br />static const int DefaultSize = 10;</p><p>public:<br />PriorityQueue(bool isMaxHeap = true) {<br />Initialize(nullptr, 0, DefaultSize, isMaxHeap);<br />}</p><p>PriorityQueue(int size, bool isMaxHeap = true) {<br />Initialize(nullptr, 0, size, isMaxHeap);<br />}</p><p>PriorityQueue(ItemType items[], int count, bool isMaxHeap = true) {<br />Initialize(items, count, count, isMaxHeap);<br />}</p><p>// Item count in queue.<br />inline int Count(void) {<br />return count;<br />}</p><p>// Only get first item.<br />inline ItemType Get() {<br />if (count == 0) {<br />// empty queue.<br />// do something here.<br />throw exception();<br />} else {<br />return *items;<br />}<br />}</p><p>// Get and remove first item.<br />ItemType Remove() {<br />if (count == 0) {<br />// empty queue.<br />// do something here.<br />throw exception();<br />}<br />return Remove(0);<br />}</p><p>// Add a new item into queue.<br />void Insert(ItemType value) {<br />TryResize(true);<br />items[count - 1] = value;<br />TryMoveUp(count - 1, value);<br />}</p><p>// Change value at index.<br />void Change(int index, ItemType value) {<br />if (index >= count) {<br />// out of range.<br />// throw exception or do nothing.<br />throw exception();<br />}</p><p>if (isMaxHeap ? value < items[index] : value > items[index]) {<br />TryMoveDown(index, value);<br />} else if (isMaxHeap ? value > items[index] : value < items[index]) {<br />TryMoveUp(index, value);<br />} else {<br />// do nothing.<br />}<br />}</p><p>ItemType Remove(int index) {<br />if (index >= count) {<br />// out of range.<br />// throw exception or do nothing.<br />throw exception();<br />}</p><p>ItemType result = items[index];<br />items[index] = items[count - 1];<br />TryResize(false);<br />if (index != count - 1) {<br />Heapify(items, index, count, isMaxHeap);<br />}<br />return result;<br />}</p><p>// Sort all items in queue to destinationArray, destinationSize for safe.<br />bool ToArray(ItemType destinationArray[], int destinationSize) {<br />if (destinationSize < count) {<br />// can not copy all items into destination array.<br />throw exception();<br />}</p><p>SortTo(destinationArray);<br />return true;<br />}</p><p>~PriorityQueue(void) {<br />delete[] items;<br />}</p><p>private:<br />void Initialize(ItemType items[], int count, int size, bool isMaxHeap) {<br />if (size < count) {<br />// throw exception or set a new size.<br />size = count + DefaultSize;<br />}</p><p>this->maxSize = size;<br />this->count = count;<br />this->isMaxHeap = isMaxHeap;<br />this->items = new ItemType[size];</p><p>if (items != nullptr) {<br />for (int i = 0; i < count; ++i) {<br />this->items[i] = items[i];<br />}<br />BuildHeap();<br />}<br />}</p><p>void BuildHeap(void) {<br />for (int i = ParentIndex(count - 1); i >= 0; --i) {<br />Heapify(items, i, count, isMaxHeap);<br />}<br />}</p><p>// move items[index] down to the right position in its subtree.<br />void Heapify(ItemType items[], int index, int count, bool isMaxHeap) {</p><p>int currentIndex = index;<br />int targetIndex = index;</p><p>if (isMaxHeap) { // ... how to do this better?<br />while (true) {<br />int leftIndex = LeftChildIndex(currentIndex);<br />int rightIndex = RightChildIndex(currentIndex);</p><p>if (leftIndex < count && items[leftIndex] > items[targetIndex]) {<br />targetIndex = leftIndex;<br />}<br />if (rightIndex < count && items[rightIndex] > items[targetIndex]) {<br />targetIndex = rightIndex;<br />}<br />if (targetIndex != currentIndex) {<br />Swap(items + targetIndex, items + currentIndex);<br />currentIndex = targetIndex;<br />} else {<br />break;<br />}<br />}<br />} else {<br />while (true) {<br />int leftIndex = LeftChildIndex(currentIndex);<br />int rightIndex = RightChildIndex(currentIndex);</p><p>if (leftIndex < count && items[leftIndex] < items[targetIndex]) {<br />targetIndex = leftIndex;<br />}<br />if (rightIndex < count && items[rightIndex] < items[targetIndex]) {<br />targetIndex = rightIndex;<br />}<br />if (targetIndex != currentIndex) {<br />Swap(items + targetIndex, items + currentIndex);<br />currentIndex = targetIndex;<br />} else {<br />break;<br />}<br />}<br />}<br />}</p><p>inline int LeftChildIndex(int parentIndex) {<br />return RightChildIndex(parentIndex) - 1;<br />}<br />inline int RightChildIndex(int parentIndex) {<br />return ++parentIndex << 1;<br />}<br />inline int ParentIndex(int index) {<br />return (++index >> 1) - 1;<br />}</p><p>void SortTo(ItemType destitionArray[]) {<br />// copy items.<br />for (int i = 0; i < count; ++i) {<br />destitionArray[i] = items[i];<br />}</p><p>// only for exercises... do heap sort in destitionArray.<br />for (int i = count, tempCount = count; i > 0; --i) {<br />Swap(destitionArray, destitionArray + tempCount - 1);<br />--tempCount;<br />Heapify(destitionArray, 0, tempCount, isMaxHeap);<br />}</p><p>}</p><p>void Swap(ItemType* a, ItemType* b) {<br />ItemType temp = *a;<br />*a = *b;<br />*b = temp;<br />}</p><p>void TryMoveUp(int index, ItemType value) {<br />if (isMaxHeap ? value < items[index] : value > items[index]) {<br />// do something about value error.<br />throw exception();<br />}<br />items[index] = value;<br />int currentIndex = index;<br />int parentIndex = ParentIndex(index);<br />while (index > 0 && (isMaxHeap ? items[parentIndex] < items[index] : items[parentIndex] > items[index])) {<br />Swap(items + index, items + parentIndex);<br />index = parentIndex;<br />parentIndex = ParentIndex(parentIndex);<br />}<br />}</p><p>void TryMoveDown(int index, ItemType value) {<br />if (isMaxHeap ? value > items[index] : value < items[index]) {<br />// do something about value error.<br />throw exception();<br />}<br />items[index] = value;<br />Heapify(items, index, count, isMaxHeap);<br />}</p><p>void TryResize(bool isIncrease) {<br />if (isIncrease) {<br />if (count >= maxSize) {<br />maxSize = (maxSize << 1) + 10;<br />NewItems(maxSize);<br />}<br />++count;<br />} else {<br />if (count < maxSize >> 1) {<br />maxSize = (maxSize >> 1) + 5;<br />NewItems(maxSize);<br />}<br />--count;<br />}<br />}</p><p>void NewItems(int size) {<br />ItemType* newItems = new ItemType[size];<br />// copy items.<br />for (int i = 0; i < count; ++i) {<br />newItems[i] = items[i];<br />}<br />delete[] items;<br />items = newItems;<br />}</p><p>private:<br />ItemType* items;<br />int count;<br />int maxSize;<br />bool isMaxHeap;<br />};<br />

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.