现在的位置: 首页 > 综合 > 正文

STL中priority_queue的使用注意事项

2018年03月22日 ⁄ 综合 ⁄ 共 1704字 ⁄ 字号 评论关闭

今天在使用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。不会产生上述问题。

抱歉!评论已关闭.