STL-08-priority_queue源码剖析

priority_queue

    • 一、相关定义
    • 二、使用方法
      • 1、普通方法:
      • 2、自定义优先级:
      • 3、结构体声明方式:
      • 4、代码使用介绍
    • 三、源码剖析

一、相关定义

    优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。

    优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container不能用来实现queue,因为list的迭代器不是任意存取iterator,而pop中用到堆排序时是要求randomaccess iterator 的!):

priority_queue<vector<int>, less<int>  > pq1;     // 使用递增less函数对象排序
priority_queue<deque<int>, greater<int>  > pq2;   // 使用递减greater函数对象排序

和队列基本操作相同:

top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容

二、使用方法

    在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。

头文件:

#include 

声明方式:

1、普通方法:

priority_queue<int> q;  //通过操作,按照元素从大到小的顺序出队
priority_queue<int,vector<int>, greater<int> > q;//通过操作,按照元素从小到大的顺序出队

2、自定义优先级:

struct cmp {     
  operator bool ()(int x, int y)     
  {        
     return x > y;   // x小的优先级高       
     //也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高
  }
};
priority_queue<int, vector<int>, cmp> q;    //定义方法

3、结构体声明方式:

struct node {     
  int x, y;     
  friend bool operator < (node a, node b)     
  {         
    return a.x > b.x;    //结构体中,x小的优先级高     
  }
};
priority_queue<node>q;   //定义方法

在该结构中,y为值, x为优先级。
通过自定义operator<操作符来比较元素中的优先级。
在重载”<”时,最好不要重载”>”,可能会发生编译错误

4、代码使用介绍

#include
#include 
#include 
#include
using namespace std;

struct cmp1 
{
	bool operator()(int &a,int &b)
	{
		return a > b;//最小值优先
	}
};


struct cmp2
{
	bool operator()(int &a, int &b)
	{
		return a < b;//最大值优先
	}
};

struct MyStruct
{
	string name;
	int age;
	bool operator  < (const MyStruct &o) const
	{

		return age > o.age;
	}

};


struct MyStruct2
{
	string name;
	int age;
	bool operator  < (const MyStruct2 &o) const
	{

		return age < o.age;
	}

};

void main()
{
	//1.默认是最大的排在最前面。
	priority_queue<int> m_queue;
	// priority_queue,less >m_queue;//和上面是一样的效果 
	m_queue.push(1);
	m_queue.push(3);
	m_queue.push(5);
	m_queue.push(2);
	m_queue.push(4);
	m_queue.push(6);

	cout << "最大的在最前面:\n";
	while (!m_queue.empty())
	{
		cout << m_queue.top() << " ";
		m_queue.pop();
	}
	cout << endl;
	cout << "最小的在最前面:\n";

	//2.如何最小的在前面呢?(greater在functional模板里)
	//注意“>>”会被认为错误, 
	//这是右移运算符,所以这里用空格号隔开 
	priority_queue<int, vector<int>, greater<int> > m_queue2;
	m_queue2.push(1);
	m_queue2.push(3);
	m_queue2.push(5);
	m_queue2.push(2);
	m_queue2.push(4);
	m_queue2.push(6);

	while (!m_queue2.empty())
	{
		cout << m_queue2.top() << " ";
		m_queue2.pop();
	}
	cout << endl;

	//3.自定义 最小值
	cout << "自定义最小值优先:\n";
	priority_queue<int, vector<int>, cmp1> m_queue3;
	m_queue3.push(7);
	m_queue3.push(9);
	m_queue3.push(11);
	m_queue3.push(8);
	m_queue3.push(10);
	m_queue3.push(12);

	while (!m_queue3.empty())
	{
		cout << m_queue3.top() << " ";
		m_queue3.pop();
	}
	cout << endl;

	//4.自定义 最大值
	cout << "自定义最大值优先:\n";
	priority_queue<int, vector<int>, cmp2> m_queue4;
	m_queue4.push(7);
	m_queue4.push(9);
	m_queue4.push(11);
	m_queue4.push(8);
	m_queue4.push(10);
	m_queue4.push(12);

	while (!m_queue4.empty())
	{
		cout << m_queue4.top() << " ";
		m_queue4.pop();
	}
	cout << endl;

	//5.自定义结构体1
	cout << "自定义结构体1:\n";
	priority_queue<MyStruct> m_queue5;
	MyStruct my1;
	my1.name = "jack1";
	my1.age = 22;
	m_queue5.push(my1);

	MyStruct my2;
	my2.name = "jack2";
	my2.age = 28;
	m_queue5.push(my2);


	MyStruct my3;
	my3.name = "jack3";
	my3.age = 25;
	m_queue5.push(my3);


	MyStruct my4;
	my4.name = "jack1";
	my4.age = 20;
	m_queue5.push(my4);


	while (!m_queue5.empty())
	{
		MyStruct temp = m_queue5.top();
		cout << temp.name.c_str() << " " << temp.age << endl;
		m_queue5.pop();
	}
	cout << endl;


	//6.自定义结构体
	cout << "自定义结构体2:\n";
	priority_queue<MyStruct2> m_queue6;
	MyStruct2 my12;
	my12.name = "jack1";
	my12.age = 22;
	m_queue6.push(my12);

	MyStruct2 my22;
	my22.name = "jack2";
	my22.age = 28;
	m_queue6.push(my22);


	MyStruct2 my32;
	my32.name = "jack3";
	my32.age = 25;
	m_queue6.push(my32);


	MyStruct2 my42;
	my42.name = "jack1";
	my42.age = 20;
	m_queue6.push(my42);


	while (!m_queue6.empty())
	{
		MyStruct2 temp = m_queue6.top();
		cout << temp.name.c_str() << " " << temp.age << endl;
		m_queue6.pop();
	}
	cout << endl;

	system("pause");
}

结果:
STL-08-priority_queue源码剖析_第1张图片

三、源码剖析

    至于优先队列的原理其实就是大顶堆、小顶堆的结构。具体实现可以看看我的另一篇文章:算法设计之-世界上最好理解的堆排序
先来看个图对它有个印象
STL-08-priority_queue源码剖析_第2张图片
接下来看看源码:

template <class T, class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
class priority_queue {
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 底層容器
Compare comp; // 元素大小比較標準
public:
priority_queue() : c() {}
explicit priority_queue(const Compare& x) : c(), comp(x) {}
// 以㆘用到的 make_heap(), push_heap(), pop_heap()都是泛型演算法
// 注意,任㆒個建構式都立刻於底層容器內產生㆒個 implicit representation heap。
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last, const Compare& x)
: c(first, last), comp(x) { make_heap(c.begin(), c.end(), comp); }
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: c(first, last) { make_heap(c.begin(), c.end(), comp); }
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
const_reference top() const { return c.front(); }
void push(const value_type& x) {
__STL_TRY {
// push_heap 是泛型演算法,先利用底層容器的 push_back() 將新元素
// 推入末端,再重排 heap。見 C++ Primer p.1195。
c.push_back(x);
push_heap(c.begin(), c.end(), comp); // push_heap 是泛型演算法
}
__STL_UNWIND(c.clear());
}
void pop() {
__STL_TRY {
// pop_heap 是泛型演算法,從 heap 內取出㆒個元素。它並不是真正將元素
// 彈出,而是重排 heap,然後再以底層容器的 pop_back() 取得被彈出
// 的元素。見 C++ Primer p.1195。
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
__STL_UNWIND(c.clear());
}
};

从源码class Sequence = vector可以看出 底层容器默认使用的是vector

priority_queue的所有元素,进出都有一定的规则,只有queue顶端的元素才有机会被外界取用,priority_queue不提供走访功能,也不提供迭代器。
其实主要也就是插入一个元素后,然后调整堆结构,使其满足大顶堆,或者小顶堆的性质。更重要的关注大小顶堆加入元素后的一个调整。
所以好好看看我刚提到的那篇文章。

你可能感兴趣的:(STL)