數據結構-堆

在看libevent時看到它用小根堆來管理時間,忽然猛地發現堆的概念有點忘了(上一次看數據結構是兩年半之前的一個暑假),所以此處複習一下。
堆是利用完全二叉樹來維護的一種數據結構,存取操作時間複雜度在O(1)-O(log n)之間,完全二叉樹就是除了最後一層外,其他所有層都是滿節點,且最後一層如果缺少節點,缺的部分應該在最右邊。
堆分爲兩種類型,大根堆與小根堆,以大根堆爲例,每個節點都小於它的父節點。假設現在隨手寫一串數字 1 , 2 , 10 , -1, 0 ,8,3
先建立成完全二叉樹

要把它初始化爲一個大頂堆的話,只需要從最後一個非葉子節點開始,依次做下沉動作。對了,操作可分爲上浮跟下沉,就是向上或向下調整到對應的位置。最後一個葉子節點索引號可用總個數/2 得到。因爲只要所有的非葉子節點位置正確,那整個堆就是正確的。此處就是10這個節點,下沉動作就是向葉子節點找一個大一點的進行比較,如果大於自己,則進行對調,對調後重複比較動作。所以10就是跟8,3比較,沒有比10 大的,不做任何改變。同樣2,也是。 到1這個節點,首先由於2<10,並且1<10所以,1和10對調

接着,由於3<8,且1<8,所以1要跟8對調。最終結果就是下面這張圖

可以看到,10大於2和8,   2大於-1和0  ,  8大於1和3 滿足大根堆的性質。

上浮動作類似,就是一直跟父節點比較,如果自己大於父節點,那就對調位置,對調後繼續跟新的父節點比較,重複這個比較動作即可。

很多書上講堆排序,其實就是做一個大根堆,然後每次取出根節點,然後把最後一個節點放到第一個,再做下沉動作。這樣保證取出一個節點後仍然爲一個大根堆,接着繼續取,繼續調整...最終節點個數爲1,結束。
 

下面給出一個代碼,其中通過>>運算符可以插入一個節點,<<運算符可以每次取根元素並調整,也就是說cout這個對象後會得到一個有序序列。
 

#include<iostream>
using namespace std;

class Big_Heap
{
public:
	void shift_down(int k) //大根堆
	{
		for (int i = 2 * k; i <= size; i *= 2) //i*2 下一次循環時就會指向當前的左孩子節點
		{		
			if (i < size && queue[i] < queue[i + 1]) //如果右孩子較大則讓i指向右孩子
				i++;
			if (queue[k] < queue[i])
			{
				std::swap(queue[i], queue[k]);
				k = i;
			}
			else
				break;  // 調整結束
		}
		
	}
	void shift_up(int k)
	{
		for (int i = k/2; i >= 1; i/=2)
		{
			if (queue[i] < queue[k])
			{
				std::swap(queue[i], queue[k]);
				k = i;
			}
		}
	}
	friend ostream & operator << (ostream &,  Big_Heap  &);
	Big_Heap& operator >> (int t)
	{
		queue[++size] = t;
		shift_up(size);
		for (int i = 1; i <= size; i++)
			cout << queue[i] << ' ';
		cout << endl;
		return *this;
	}
	void pop()         //彈出操作 
	{
		int temp = queue[1];
		queue[1] = queue[size];
		queue[size] = temp;
		size--;
		shift_down(1);
	}
	int top() { return queue[1]; }
private:
	int size = 0;
	int queue[1024];
};
ostream & operator << (ostream & out,  Big_Heap  & obj)
{
	const int num = obj.size;//下面不斷在pop,會改變size的值,所以要先保存下
	for (int i = 1; i <= num; i++)
	{
		out << obj.top() << " ";  //輸出堆頂元素 
		obj.pop();        //彈出堆頂元素 
	}
	return out; 
}

int main()
{
	Big_Heap obj;
	obj >> 1 >> 2 >> 10 >> -1 >> 0 >> 8 >> 3;
	
	cout << obj;
	system("pause");
}

這是大根堆的實現方式,小根堆就只需要改幾個符號即可,不再重複貼出代碼

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章