STL中priority_queue的使用注意事項

今天在使用priority_queue寫一個算法的時候,總是報heap異常,查了很久,才知道原因。

我們知道優先級隊列的底層是用heap來實現的,每次push和pop操作後,都會調用heapify來調整最大堆得結構。

我們先來看看priority_queue的聲明:

priority_queue<stack<Activity>, vector<stack<Activity>>, cmp> pq;


這是我使用的一個優先級隊列,隊列裏每個元素都是一個stack,第一個參數就是元素的類型,第二個參數是指定優先級隊列使用什麼容器,如果不指定,默認就是vector。下面是priority_queue的模板聲明:

template<class _Ty,
	class _Container = vector<_Ty>,
	class _Pr = less<typename _Container::value_type> >


我們可以看到,第三個模板參數就是比較函數了,指定優先級隊列中的比較方法。這個比較方法必須是strick weak order的,不知道的同學可以上網查一查。這個非常重要,以後每次調整heap前,都會檢查現有隊列是否是strick weak order。下面是我的比較方法:

struct cmp
{
	bool operator()(stack<Activity> &q1, stack<Activity> &q2)
	{
		if(q1.top().start < q2.top().start)
			return true;
		return false;
	}
};


如果你優先級隊列中的元素是一個容器的話(比如vector,stack,queue之類的),就需要特別注意了,如果你要向優先級隊列中的元素(這個元素是一個容器)中插入元素的話,我的建議是先把該容器整個pop出來,然後更新該容器,再push進去,代碼如下:

Activity a(s[i], f[i]);
stack<Activity> st = pq.top();
st.push(a);
pq.pop();
pq.push(st);


你會想,這樣寫會更簡便啊:

Activity a(s[i], f[i]);
pq.top().push(a);


這會引發一個嚴重的錯誤,因爲你插入的元素並沒有調用priority_queue的push操作,所以並不會更新其隊列的順序,這樣,隊列中的順序就可能已經錯亂了,你會說:下次調用push的時候不就會重新調整好heap的順序麼,下面我們來看priority_queue的源碼:

	void push(value_type&& _Val)
		{	// insert element at beginning
		c.push_back(_STD move(_Val));
		push_heap(c.begin(), c.end(), comp);
		}

我們在進入push_heap這個agorithm中的算法(注意這是一個通用算法):

template<class _RanIt,
	class _Pr> inline
	void push_heap(_RanIt _First, _RanIt _Last, _Pr _Pred)
	{	// push *(_Last - 1) onto heap at [_First, _Last - 1), using _Pred
	_DEBUG_RANGE(_First, _Last);
	_DEBUG_POINTER(_Pred);
	if (_First != _Last)
		{	// check and push to nontrivial heap
		--_Last;
		_DEBUG_HEAP_PRED(_First, _Last, _Pred);
		_Push_heap_0(_Unchecked(_First), _Unchecked(_Last), _Pred,
			_Dist_type(_First), _Val_type(_First));
		}
	}

注意,在push_heap之前,他會進行_DEBUG_HEAP_PRED檢查,就是檢查堆中的元素是否按照你給定的順序進行排序的,如果不是,直接返回一個異常。所以,我們在想priority_queue中的容器中插入數據時,先pop,再修改,最後push。不會產生上述問題。



發佈了66 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章