1. 程式人生 > >優先佇列和堆,與priority_queue的學習筆記

優先佇列和堆,與priority_queue的學習筆記

今天花了一天的時間把優先佇列和堆,還有priority_queue給學習了學習,現在做下筆記。學完認識到優先佇列就是堆的一種,而priority_queue是為優先佇列準備的STL,定義與使用起來十分方便。


先說堆,堆是一個二叉樹,以小根堆為例(大根堆就相反了)兒子的值一定不小於父親的值,可用陣列儲存,插入數值時先在堆的末尾插入該數值,然後不斷向上交換,將優先順序大(即數值較小的)的交換上去直到沒有大小顛倒為止。

而取出元素,是取根部,換成陣列,就是下標為零那個。刪除時,首先把堆的最後一個節點的數值複製到根節點上,並且刪除最後一個節點,然後不斷向下交換,直到沒有大小顛倒為止。如果兩個兒子都比根小,則選擇小的那個兒子進行交換

下面是根的實現例項

//堆的建立與使用
#include<iostream>
using namespace std;
const int MAX_N=100;
int heap[MAX_N],sz=0;
void push(int x)
{
	int i=sz++;
	while(i>0)
	{
		int p=(i-1)/2;
		if(heap[p]>x)
		{
			heap[i]=heap[p];
			i=p;
		}
		else
			break;
	}
	heap[i]=x;
 } 
 
 int pop()
 {
 	int ret=heap[0];
 	int x=heap[--sz];
 	int i=0;
 	while(i*2+1<sz)
 	{
 		int a=i*2+1;
 		int b=i*2+2;
 		if(b<sz && heap[b]<heap[a])
 			a=b;
 		if(heap[a]>=x)
 			break;
 		heap[i]=heap[a];
 		i=a;
	 }
	 heap[i]=x;
	 return ret;
 }
 
 int main()
 {
 	int i;
 	push(1);
 	push(3);
 	push(2);
 	push(5);
 	push(6);
 	for(i=0;i<sz;i++)
 	{
 		cout<<heap[i]<<endl;
	 }
	cout<<"pop:"<<pop()<<endl;
	cout<<"pop:"<<pop()<<endl;
	cout<<"pop:"<<pop()<<endl;
	for(i=0;i<sz;i++)
	{
		cout<<heap[i]<<endl;
	}
	return 0;
 }

優先佇列

再說說優先佇列。

優先佇列能夠完成的就是能插入一個數值,但每次取出的數值一定是最小的(或最大的,看個人需求)。我先開始用vector容器,寫了個佇列,然後每次插入資料前的時候用 lower_bound 函式檢測要插入的位置,最後將資料插入指定的位置。當然這個時間可能用的比較長,但這是我一開始就想到並實踐的方法,優點是簡單

此處為我實現的筆記,各位看官可忽略

#include<iostream>  
#include<vector>  
#include<algorithm>  
using namespace std;  
bool cmp(int a,int b)  
{  
	return a>b;  
}   
      
int main()
{
	vector <int> v;
	vector <int>::iterator it;
	int n,i;
	cin>>n;			//此處輸入即將輸入資料的個數 
	for(i=0;i<n;i++)
	{
		int vi;
		cin>>vi;
		it=lower_bound(v.begin(),v.end(),vi,cmp);
		v.insert(it,vi);
	}
	while(!v.empty())		//這樣每次尾部的函式是最小的函式 
	{
		cout<<v.back()<<" ";	 
		v.pop_back();	//每次彈出的也是尾部的函式 
	}
	cout<<endl;
	return 0;
 } 

priority_queue的使用

這個是優先佇列的STL,是以堆的形式進行儲存,如果不是自己定義的資料結構,有兩種,一個是大根堆,即取出的數一定是最大的,另一個是以小根堆的定義

大根堆:priority_queue<int> max_q;

小根堆:priority_queue<int,vector<int>,greater<int> >min_q;  (親測最後"greater<int> >"處空格不能少,不然編譯錯誤。原因大概是跟>>運算子發生歧義

#include<iostream>
#include<queue> 
using namespace std;
int main()
{
	priority_queue<int> max_q;	//大根堆
	priority_queue<int,vector<int>,greater<int> > min_q;	//小根堆 
	int n,i,temp;
	cin>>n;
	for(i=0;i<n;i++)
	{
		cin>>temp;
		max_q.push(temp);
		min_q.push(temp);
	 } 
	 cout<<endl;
	 //大根堆的輸出 
	 while(!max_q.empty())
	 {
	 	cout<<max_q.top()<<" ";
	 	max_q.pop();
	  } 
	  cout<<endl;
	  
	  while(!min_q.empty())
	  {
	  	cout<<min_q.top()<<" ";
	  	min_q.pop();
	  }
	  cout<<endl;
	return 0;
}

下面是含有自己定義的資料結構struct時,進行優先佇列使用的方法

有兩種方法。第二種方法我給註釋掉了。用第二種方法的時候別忘了將第一種方法的部分註釋掉

//優先佇列的研究2:資料結構的優先佇列方法
#include<iostream>
#include<queue>

using namespace std;
struct time
{
	int hour;
	int minute;
	int second;
	
	//第一種過載方法,結構體內過載,當如此過載時,優先佇列的定義方法:priority_queue<time> q;
	friend bool operator < (time t1,time t2)	//此處運算子過載
	{
		if(t1.hour==t2.hour)
		{
			if(t1.minute==t2.minute)
			{
				return t1.second<t2.second;
			}
			else
				return t1.minute<t2.minute;
		}
		else
			return t1.hour<t2.hour;
	 } 
	 //最終的效果會是一個大根堆 
};

/*第二種過載方法,結構體外過載比較函式 
struct cmp{
	bool operator()(time t1,time t2)
	{
		if(t1.hour==t2.hour)
		{
			if(t1.minute==t2.minute)
			{
				return t1.second<t2.second;
			}
			else
				return t1.minute<t2.minute;
		}
		else
			return t1.hour<t2.hour;
		
	}
};
*/

int main()
{
	priority_queue<time> q;
	//priority_queue<time,vector<time>,cmp > q;		//當使用類外過載比較函式的時候優先佇列要如此定義 
	int n,i;
	time ti;
	cin>>n;
	for(i=0;i<n;i++)
	{
		cin>>ti.hour>>ti.minute>>ti.second;
		q.push(ti);
	}
	while(!q.empty())
	{
		ti=q.top();
		cout<<ti.hour<<":"<<ti.minute<<":"<<ti.second<<endl;
		q.pop();
	}
	return 0;
 } 

大概就這麼多,如果日後有什麼缺少的我還會補充的