利用堆实现堆排序优先队列
數據結構之(二叉)堆一文在末尾提到“利用堆可以實現:堆排序、優先隊列?!薄1疚拇a實現之。
1、堆排序
假設要實現非遞減排序,則需要用要大頂堆。此處設計到三個大頂堆的操作:(1)自頂向下調整操作:MaxHeapify(對應堆的SiftDown操作)、(2)利用數組建立大頂堆:BuildMaxHeap、(3)不斷交換堆頂元素(堆的最大元素)和堆的末尾元素,實現非遞減排序。
下面是具體的實現代碼:
//已知L[i,...,n)除L[i]之外均滿足大頂堆的定義,本函數向下調整L[i] //使得在具有n個結點的堆中,以i為下標的根節點的子樹重新遵循最大堆的性質 //n為節點總數,從0開始計算,i節點的子節點為 2*i+1, 2*i+2 void MaxHeapify(int L[], int i, int n) {int j, tmp;j = 2 * i + 1; //父節點i的左孩子結點tmp = L[i];while (j < n){if (j + 1 < n && L[j + 1] > L[j])//找左右孩子中最大的j++;if (L[j] <= tmp) // 父結點tmp=L[i]比孩子結點L[j]大break;L[i] = L[j]; //把較大的子結點往上移動,替換它的父結點i = j;j = 2 * i + 1;}L[i] = tmp; }//子數組L[n/2+1,..,n)中的元素都是樹的葉子結點,每個葉節點都可以看成只包含一個元素的堆。 //該函數對樹中的其余結點都調用一次MaxHeapify() void BuildMaxHeap(int L[], int n) {for (int i = n / 2 - 1; i >= 0; i--) //注意是從最后一個結點(n-1)的父節點((n-1)-1)/2 = (n-2)/2 = n/2-1 開始MaxHeapify(L, i, n); }
void MaxHeapSort(int L[], int n) {BuildMaxHeap(L, n); //先建立大頂堆for (int i = n - 1 ; i > 0; i--) //從最后一個元素起{//L[0]永遠是大頂堆堆L[0,i]的堆頂元素,最大值std::swap(L[0], L[i]); //將L[0]和最末尾元素交換后:最大的元素現在位于數組末尾,堆的結點個數減一//但L[0..i)因為修改了L[0]可能會破壞堆的性質,因此需要調整L[0,...,i)使之依然滿足最大堆://已知L[0,...,i)除L[0]之外均滿足大頂堆的定義,本函數向下調整L[0]//使得在具有i個結點的堆中,以0為下標的根節點的子樹重新遵循最大堆的性質MaxHeapify(L, 0, i);} }
2、利用堆實現優先隊列
優先隊列分為最大優先隊列和最小優先隊列,分別借助于大頂堆和小頂堆。
優先隊列有以下基本操作:(1)提取隊列中的最大(小)元素;(2)提取隊列中的最大(小)元素并從隊列中刪除;(3)將隊列中元素為x的關鍵字減少(增大)到k,這里假設k的值不大(小)于x的原關鍵值。其他的還包括如插入、刪除操作。這些操作大多調用SiftDown、SiftUp操作實現,下面是具體實現代碼:
(1)最大優先隊列
#pragma once #include "maxHeap.h"template<class T> class MaxPriQueue : public MaxHeap<T> { public:MaxPriQueue(const int nmax = 20) : MaxHeap(nmax) {}~MaxPriQueue(){}T maximum() const; //返回具有最大鍵值的元素T ExtractMax(); //去掉并返回最大鍵值的元素void InCreaseKey(const T &x, const T &k); //將元素x的關鍵字增加到k,這里假設k的值不小于x的原關鍵值virtual void Insert(const T &key);//算法導論第三版p92的方法;也可以調用繼承來的Insert方法 };template<class T> T MaxPriQueue<T>::maximum()const {if (size > 0)return arr[0];return T(0); }template<class T> T MaxPriQueue<T>::ExtractMax() {if (size < 0)return T(0);T max = arr[0];arr[0] = arr[--size];//最末尾的值補上去,同時size減1SiftDown(0); //向下調整return max; }template<class T> void MaxPriQueue<T>::InCreaseKey(const T &x, const T &k) {if (k < x) //假設k的值不小于x的原關鍵值return;int pos = Search(x);if (pos == -1)return;arr[pos] = k;SiftUp(pos); //向上調整 } template<class T> void MaxPriQueue<T>::Insert(const T &key) {arr[size++] = -INFINITY; //首先增加一個-∞的葉節點InCreaseKey(-INFINITY, key); //將該葉節點的值增大至key }(2)最小優先隊列
#pragma once #include "minHeap.h"template<class T> class MinPriQueue : public MinHeap<T> { public:MinPriQueue(const int nmax = 20) : MinHeap(nmax) {}~MinPriQueue(){}T minimum() const; //返回具有最小鍵值的元素T ExtractMin(); //去掉并返回最小鍵值的元素void DeCreaseKey(const T &x, const T &k); //將元素x的關鍵字減少到k,這里假設k的值不大于x的原關鍵值virtual void Insert(const T &key);//算法導論第三版p92的方法;也可以調用繼承來的Insert方法 };template<class T> T MinPriQueue<T>::minimum()const {if (size > 0)return arr[0];return T(0); }template<class T> T MinPriQueue<T>::ExtractMin() {if (size < 0)return T(0);T min = arr[0];arr[0] = arr[--size];//最末尾的值補上去,同時size減1SiftDown(0); //向下調整return min; }template<class T> void MinPriQueue<T>::DeCreaseKey(const T &x, const T &k) {if (k >= x) //假設k的值不大于x的原關鍵值return;int pos = Search(x);if (pos == -1)return;arr[pos] = k;SiftUp(pos); //向上調整 } template<class T> void MinPriQueue<T>::Insert(const T &key) {arr[size++] = INFINITY; //首先增加一個∞的葉節點DeCreaseKey(INFINITY, key); //將該葉節點的值減少至key }
運行截圖:
ps:代碼中的繼承的類的代碼可以在?數據結構之(二叉)堆?中查看;
? ? ? ? 向量排序算法:不斷將無序數組中的元素插入最小優先隊列中,構建完畢后,再調用n次ExtractMin()操作,也可以實現非遞減排序,但是要花費O(n)的空間復雜度。(編程珠璣:p148)
3、STL中的堆(heap)
STL中與堆相關的4個函數——建立堆make_heap()、在堆中添加數據push_heap()、在堆中刪除數據pop_heap()和堆排序sort_heap()。
頭文件 #include <algorithm>
下面的_First與_Last為可以隨機訪問的迭代器(指針),_Comp為比較函數(仿函數),其規則——如果函數的第一個參數小于第二個參數應返回true,否則返回false。
(1)建立堆
make_heap(_First, _Last, _Comp)
默認是建立大頂堆的(less<T>())。對int類型,可以在第三個參數傳入greater<int>()得到最小堆,最小堆的時候的push_heap()、pop_heap()、sort_heap()均要顯示地將greater<int>()作為第三個參數傳入。
(2)在堆中添加數據
push_heap (_First, _Last)
要先在容器中加入數據:push_back(x)操作;再調用push_heap ():將最末尾新添加的元素向上調整使之整個容器數組符合堆性質。
(3)在堆中刪除數據
pop_heap(_First, _Last)
要先調用pop_heap()操作:將堆頂元素和堆的最末尾元素交換,堆大小減去1,并向下調整堆頂元素使得新的堆滿足最大堆的性質;再在容器中刪除數據:pop_back()操作。每次刪除的是堆頂的元素
(4)堆排序
sort_heap(_First, _Last)
排序之后就不再是一個合法的heap了。
STL中heap使用示例代碼:
#include <iostream> #include <vector> #include <algorithm> #include <functional> //for function object like greater<int>() #include <ctime> using namespace std;void print(vector<int> &vet) {for (vector<int>::const_iterator iter = vet.begin(); iter != vet.end(); iter++)cout << *iter << " ";cout << endl; } int main() {const int MAXN = 10;int a[MAXN];srand((unsigned)time(0));for (int i = 0; i < MAXN; ++i)a[i] = rand() % (MAXN * 2);//動態申請vector,并對vector建堆vector<int> *pvet = new vector<int>(20);pvet->assign(a, a + MAXN);cout << "\n無序數組:\t\t";print(*pvet);//默認建大頂堆make_heap(pvet->begin(), pvet->end());cout << "大頂堆:\t\t\t";print(*pvet);//添加數據pvet->push_back(25); //先在容器中加入cout << "\n容器push_back(25)后:\t";print(*pvet);push_heap(pvet->begin(), pvet->end()); //再調用push_heap()cout << "堆push_heap()操作后:\t";print(*pvet);//刪除數據pop_heap(pvet->begin(), pvet->end()); // 先調用pop_heap()cout << "\n堆pop_heap()操作后:\t";print(*pvet);pvet->pop_back(); //再在容器中刪除cout << "容器pop_back()后:\t";print(*pvet);pop_heap(pvet->begin(), pvet->end());cout << "\n堆pop_heap()操作后:\t";print(*pvet);pvet->pop_back();cout << "容器pop_back()后:\t";print(*pvet);//堆排序sort_heap(pvet->begin(), pvet->end());cout << "\n堆排序結果:\t\t";print(*pvet);delete pvet;return 0; } 示例截圖:
4、利用堆求解top_k
編寫算法,從10億個浮點數當中,選出其中最大的10000個。
參考資料:數據結構p297-p283、算法導論第三版:p90-p92、編程珠璣:p145-p148
? ? ? ? ? ? ? ? ? ? STL系列之四 heap 堆
總結
以上是生活随笔為你收集整理的利用堆实现堆排序优先队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle PL/SQL中EXCEPT
- 下一篇: 基于GEE平台的植被覆盖度(FVC)像元