日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用堆实现堆排序优先队列

發布時間:2024/1/18 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用堆实现堆排序优先队列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據結構之(二叉)堆一文在末尾提到“利用堆可以實現:堆排序、優先隊列?!薄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 }


測試代碼:

#include "max_priqueue.h" #include "min_priqueue.h" #include <iostream> using namespace std;int main() {int a[12] = {15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1};int b[12]; memcpy(b, a, sizeof(a));cout << "原始數組元素: ";for (int i = 0; i < 12; i++)cout << a[i] << " ";cout << endl;MaxPriQueue<int> maxpriqueue(20);MinPriQueue<int> minpriqueue(20);for (int i = 0; i < 12; i++)maxpriqueue.Insert(a[i]); //向上調整cout << "\n最大優先隊列元素為: "; maxpriqueue.Print();cout << "max = " << maxpriqueue.maximum() << endl;cout << "刪掉最大元素 " << maxpriqueue.ExtractMax() << "后 :" ; maxpriqueue.Print();cout << "將元素 12 增加到 23 后:"; maxpriqueue.InCreaseKey(12, 23);maxpriqueue.Print();cout << "刪掉元素4后: "; maxpriqueue.Delete(4); maxpriqueue.Print();for (int i = 0; i < 12; i++)minpriqueue.Insert(a[i]); //向上調整cout << "\n最小優先隊列元素為: "; minpriqueue.Print();cout << "min = " << minpriqueue.minimum() << endl;cout << "刪掉最小元素 " << minpriqueue.ExtractMin() << "后 :" ; minpriqueue.Print();cout << "將元素 12 減小到 3 后:"; minpriqueue.DeCreaseKey(12, 23);minpriqueue.Print();cout << "刪掉元素4后: "; minpriqueue.Delete(4); maxpriqueue.Print();getchar();return 0; }
運行截圖:



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個。

//典型的Top K問題,用堆是最典型的思路。 //建10000個數的小頂堆,然后將10億個數依次讀取,大于堆頂,則替換堆頂,做一次堆調整。 //結束之后,小頂堆中存放的數即為所求。代碼如下(為了方便,這里直接使用了STL容器):void top_k() {const int nmax = 1000000000; //10億個數vector<float>bigs(10000, 0);//init vector arraysrand((unsigned)time(0));for (vector<float>::iterator iter = bigs.begin(); iter != bigs.end(); iter++)*iter = (float)rand() / 7; //random values;//cout << bigs.size() << endl;make_heap(bigs.begin(), bigs.end(), greater<float>());//little heap, the first one is the smallest one!float f;for (int i = 0; i < nmax; i++){srand((unsigned)time(0));f = (float)rand() / 7;if (f > bigs.front())//replace the first element?{//set the smallest one to the end!pop_heap(bigs.begin(), bigs.end(), greater<float>());//remove the smallest onebigs.pop_back();//add to the last onebigs.push_back(f);//make the heap again, the first element is still the smallest onepush_heap(bigs.begin(), bigs.end(), greater<float>());}}//sort by ascentsort_heap(bigs.begin(), bigs.end(), greater<float>()); }

參考資料:數據結構p297-p283、算法導論第三版:p90-p92、編程珠璣:p145-p148

? ? ? ? ? ? ? ? ? ? STL系列之四 heap 堆


總結

以上是生活随笔為你收集整理的利用堆实现堆排序优先队列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。