C++ 資料結構-堆

_Gion發表於2017-08-12

堆--一種可被視為完全二叉樹的結構,實現有多種方法





(一)  C++ STL - 優先佇列實現

1.首先寫好佇列標頭檔案

#include<queue>


2.定義一個int型、值小的數優先順序高(先出佇列)的佇列-----小根堆

*最後的'<int>' 與‘>’間注意留空格

priority_queue<int, vector<int>, greater<int> > a;
大根堆的定義:
priority_queue<int, vector<int> > a;
//priority_queue<int, vector<int>, less<int> > a;


3.操作:

往堆中加一個元素:

a.push(x);
彈出堆頂元素:

a.pop();
訪問堆頂元素:

a.top();
堆的大小:a.size()

判斷堆是否為空:a.empty();


4.一個經典的栗子:合併果子

題目描述

在一個果園裡,多多已經將所有的果子打了下來,而且按果子的不同種類分成了不同的堆。多多決定把所有的果子合成一堆。

每一次合併,多多可以把兩堆果子合併到一起,消耗的體力等於兩堆果子的重量之和。可以看出,所有的果子經過n-1次合併之後,就只剩下一堆了。多多在合併果子時總共消耗的體力等於每次合併所耗體力之和。

因為還要花大力氣把這些果子搬回家,所以多多在合併果子時要儘可能地節省體力。假定每個果子重量都為1,並且已知果子的種類數和每種果子的數目,你的任務是設計出合併的次序方案,使多多耗費的體力最少,並輸出這個最小的體力耗費值。

例如有3種果子,數目依次為1,2,9。可以先將1、2堆合併,新堆數目為3,耗費體力為3。接著,將新堆與原先的第三堆合併,又得到新的堆,數目為12,耗費體力為12。所以多多總共耗費體力=3+12=15。可以證明15為最小的體力耗費值。

輸入格式:

輸入包括兩行,第一行是一個整數n(1<=n<=10000),表示果子的種類數。第二行包含n個整數,用空格分隔,第i個整數ai(1<=ai<=20000)是第i種果子的數目。

輸出格式:

輸出包括一行,這一行只包含一個整數,也就是最小的體力耗費值。輸入資料保證這個值小於2^31。

輸入樣例:
3 
1 2 9 
輸出樣例:
15










 我的程式碼:


#include <iostream>
#include <queue>
using namespace std;

priority_queue<int, vector<int>, greater<int> > a;
int n, t, ans, x, y;

int main() {
	cin >> n;
	for(int i=1; i<=n; i++) {
		cin >> t;
		a.push(t);
	}
	for(int i=1; i<=n-1; i++) {     // n個果子 合併n-1次  
		x = a.top();
		a.pop();
		y = a.top();
		a.pop();	        //合併兩堆成為新的一堆 
		a.push(x+y);
		ans += x+y;
	}
	cout << ans << endl;
	return 0;
}

(二)自定義結構體-陣列實現

在寫堆之前,首先要了解堆的性質:

設(從1儲存的)陣列heap,元素個數為heap_size; 


- heap[1]表示堆頂;

- 如果一個有中間結點是i,那麼它的左孩子的下標就是2*i,右孩子的下標就是2*i+1,父親是2*i

- 如果一個結點i,1 <= i <= heap_size/2  那麼它有孩子;  heap_size/2 < i <= heap_size 則結點i為葉子結點


小根堆:heap[i/2] <= heap[i];

大根堆:heap[i/2] >=heap[i];


下面以小根堆討論:

put()函式:在最後一個位置加入元素,迴圈與父結點比較,若小於父結點則互換,直到大於等於父結點或到了根結點

get()函式:取走堆頂端元素,將最後一個元素覆蓋根,迴圈與孩子比較,與左右(二或一個)孩子中較小的互換,直到小於等於孩子或到了葉子結點



合併果子用這種方法

程式碼(覺得不夠簡潔):

#include <iostream>
#include <climits>       //INT_MAX
using namespace std;

struct Heap {
	int heap_size;
	int a[10001];
	bool empty(void) const {
		if(heap_size == 0) return true;
		return false;
	}
	void put(int x) {
		int index = ++ heap_size;
		a[index] = x;
		while(true) {
			if(index == 1) break;
			if(a[index] < a[index/2]) {
				swap(a[index], a[index/2]);
				index /= 2;
			} else break;
		}
	}
	int get(void) {
		int rtn = top();
		int index = heap_size --;
		a[1] = a[index];
		a[index] = 0;
		index = 1;
		while(index * 2 <= heap_size) {
			int tmpx = a[2*index], tmpy = INT_MAX;
			if(2*index+1 <= heap_size) tmpy = a[2*index+1];
			if(tmpx < tmpy) {
				if(a[index] > a[index*2]) {
					swap(a[index], a[index*2]);
					index *= 2;
				} else if(a[index] > tmpy) {
					swap(a[index], a[index*2+1]);
					index = index * 2 + 1;
				} else break;

			} else {
				if(a[index] > a[index*2+1]) {
					swap(a[index], a[index*2+1]);
					index = 2*index + 1;
				} else if(a[index] > tmpx) {
					swap(a[index], a[index*2]);
					index *= 2;
				} else break;
			}
		}
		return rtn;
	}
};

Heap a;
int n, t;
int ans;

int main() {
	cin >> n;
	for(int i=1; i<=n; i++) {
		cin >> t;
		a.put(t);
	}
	for(int i=1; i<=n-1; i++) {
		int x, y;
		x = a.get();
		y = a.get();
		a.put(x+y);
		ans += x+y;
	}
	cout << ans << endl;
	return 0;
}


2018年2月重構程式碼-小根堆:

#include <iostream>
#include <cstring>
using namespace std;

struct Heap{ //小根堆 
	int a[100010];
	int size, end;
	Heap() {size = 0; fill(a+1, a+100000, 1e9);}
	int top() {
		return a[1];
	}
	void down(int x) {
		int l=x*2, r = x*2+1;
		if(a[l] < a[x] || a[r] < a[x]) {
			if(a[l] < a[r]) {
				swap(a[l], a[x]);
				down(l);
			}else {
				swap(a[r], a[x]);
				down(r);
			}
		}
	}
	void up(int K) {
		while(K > 1 && a[K] < a[K>>1]) {
			swap(a[K], a[K>>1]);
			K >>= 1;
		}
	}
	int insert(int v) {
		a[++size] = v;
		up(size);
	}
	int pop() {
		a[1] = a[size];
		a[size] = 1e9;
		down(1);
	}
}; 

int main() {
	Heap h;
	int n, tmp;
	cin >> n;
	for(int i=n; i; i--) {
		cin >> tmp;
		h.insert(tmp);
	} 
	for(int i=1; i<=n; i++) {
		cout << h.top() << ' ';
		h.pop();
	}
	return 0;
}


重構×3

struct PriorityQueue { //小根堆
	int A[100010];
	int size;
	bool empty() {
		return !size;
	}
	void push(int x) {
		int i = ++size;
		while(i>>1) {
			if(x >= A[i>>1]) break;
			A[i] = A[i>>1];
			i >>= 1;
		}
		A[i] = x;
	}
	int top() {
		if(empty()) return -1e9;
		return A[1];
	}
	int pop() {
		int res = A[1], x = A[size--];
		int i = 1;
		while((i<<1) <= size) {
			int miv = i<<1;
			if((i<<1|1) <= size && A[miv] > A[i<<1|1])
				miv = i<<1|1;
			if(A[miv] >= x) break;
			A[i] = A[miv];
			i = miv;
		}
		A[i] = x;
		return res;
	}
} heap;


(%二叉堆-Binary_Heap%!)

相關文章