明天就要去參加百度的筆試了,現在來抱抱佛教。
理論來自:簡明現代魔法
在程式設計相關領域,堆(Heap)的概念主要涉及到兩個方面:
- 一種資料結構,邏輯上是一顆完全二叉樹,儲存上是一個數組對象(二元堆積)。
- 垃圾收集儲存區,是軟體系統可以編程的記憶體地區。
本文所說的堆,指的是前者。
堆排序的時間複雜度是O(nlgN),與快速排序達到相同的時間複雜度。但是在實際應用中,我們往往採用快速排序而不是堆排序。這是因為快速排序的一個好的實現,往往比堆排序具有更好的表現。堆排序的主要用途,是在形成和處理優先順序隊列方面。另外,如果計算要求是類優先順序隊列(比如,只要返回最大或者最小元素,只有有限的插入要求等),堆同樣是很適合的資料結構。
基礎知識
堆一般用數組表示,比如數組A數組的長度Length(A),堆在數組中的元素個數HeapSize(A)。一般說來,HeapSize(A) <= Length(A),因為數組A當中可能有一些元素不在堆中。
假設節點I是數組A中下標為i的節點。
- Parent(i) : return Floor(i/2); //I的父節點下標,Floor(i)表示比i小的最大整數。
- Left(i) : return 2*i; //I的左子節點
- Right(i) : return 2*i+1; //I的右子節點
含有n個元素的堆A的高度是: Floor(lgn)。
堆的基本操作
- MaxHeapify( A, i ):
保持堆的性質。假設數組A和下標i,假定以Left(i)和Right(i)為根結點的左右兩棵子樹都已經是最大堆,節點i的值可能小於其子節點。調整節點i的位置。
- BuildMaxHeap( A ):
從一個給定的數組建立最大堆。子數組A[ floor(n/2)+1 .... ... n]中的元素都是樹的分葉節點(完全二叉樹的基本性質)。從索引 ceiling(n/2)開始一直到1,對每一個元素都執行MaxHeapify,最終得到一個最大堆。
- 堆排序 HeapSort( A ):
堆排序演算法的基本思想是,將數組A建立為一個最大堆,然後交換堆的根(最大元素)和最後一個分葉節點x,將x從堆中去掉形成新的堆A1,然後重複以上動作,直到堆中只有一個節點。
- 優先順序隊列演算法-增加某元素的值(優先順序) : HeapIncreaseKey( A, i, key )
增加某一個元素的優先順序後(元素的值),該元素應該向上移動,才能保持堆的性質。
- 優先順序隊列演算法-插入一個元素: Insert( S, x ) 將x元素插入到優先順序隊列S中。
主要思路是,將堆的最後一個分葉節點之後,擴充一個為無窮小的新分葉節點,然後增大它的值為x的值。
本來我只打算隨便寫個小程式的,但是發現,可以寫個更通用的程式。以最小堆為例,說下建堆和排序的過程。
經過上面兩個步驟,就成功的建立了一個最小堆。排序的過程就是取出堆頂元素push到臨時數組,然後將堆頂元素和最後一個元素交換,再pop掉最後一個元素,直到堆中沒有元素。這樣就獲得了一個有序的數組,然後在複製到堆中。
完整程式如下:
//最小堆排序和最大堆排序#include <algorithm>#include <functional>#include <vector>#include <iostream>using namespace std;template<typename Type>class Heap{public: Heap(const vector<Type>& a_array) { m_array.assign(a_array.begin(),a_array.end()); } template<typename Compare> void sort(Compare comp); void printArray(const vector<Type>& a_array);private: vector<Type> m_array; //comp 為less<Type> 則大數下沉,建立最小堆,從小到大排序 //comp 為greater<Type> 則小數下沉,建立最大堆,從大到小排序 template<typename Compare> void creatHeap(Compare comp); //建立堆 template<typename Compare> void downElement(int a_elem, Compare comp); //下沉元素};template<typename Type>template<typename Compare>void Heap<Type>::sort(Compare comp){ printArray(m_array); creatHeap(comp); //建堆 vector<Type> array; for (int i = m_array.size() - 1; i >= 0; i--) { array.push_back(m_array[0]); //保留堆頂 swap(m_array[0], m_array[i]); //交換 m_array.pop_back(); //去掉最後一個元素 downElement(0,comp); //將新的首元素下沉 } printArray(array); m_array.assign(array.begin(),array.end());}template<typename Type>template<typename Compare>void Heap<Type>::creatHeap(Compare comp){ //從最後一個非葉子節點開始,將每個父節點都調整為最小堆 for (int i=m_array.size()/2-1; i>=0; i--) { downElement(i, comp); }}template<typename Type>template<typename Compare>void Heap<Type>::downElement(int a_elem, Compare comp) //下沉元素{ int min; //設定最小元素下標 int index = a_elem; //當前下沉的元素下標 while (index*2+1 < m_array.size())//存在左節點 { min = index*2+1; if (index*2+2 < m_array.size())//存在右節點 { //左右節點比較,選出最小的 if (comp(m_array[index*2+2],m_array[min])) { min = index*2+2; } } //同子節點比較,若父節點最小則結束 if (comp(m_array[index],m_array[min])) { break; } else//選最小元素到父節點 { swap(m_array[min],m_array[index]); index = min; } }}template<typename Type>void Heap<Type>::printArray(const vector<Type>& a_array){ for (int i=0; i<a_array.size(); i++) { cout << a_array[i] << " "; } cout << endl;}int main(){ vector<int> array; for (int i=10; i<20; i++) { array.push_back(i); } random_shuffle(array.begin(), array.end());//打亂順序 Heap<int> heap(array); heap.sort(less<int>()); heap.sort(greater<int>()); return 0;}