C++ queue、priority_queue源码解析

  • 重点总结:
  • std::queue 是一个先进先出(FIFO)容器适配器,故 std::queue 不提供元素的任何迭代器操作。
  • std::dequestd::list 满足FIFO操作需求。queue底层默认使用std::deque
  • std::queue 容器只能访问头元素和尾元素。只能在容器的末尾添加新元素,只能从头部移除元素。
  • std::priority_queue 是一种容器适配器,根据严格的弱排序准则,它的第一个元素总是它所包含的元素中最大的。
  • std::priority_queue 默认情况下,如果没有为特定优先级队列类实例化指定容器类,底层默认使用std::vector

在这里插入图片描述


1. std::queue

1.1 构造函数

  std::queue底层容器可以是标准容器类模板之一,也可以是其他特定设计的容器类。该底层容器应至少支持以下操作:
  empty(),size(),front(),back(),push_back(),pop_front(),STL中dequelist满足这些要求。默认情况下,如果没有为特定队列类实例化指定容器类,则使用标准容器deque

  • #include<queue>

  • queue 构造函数有如下几种类型www.cplusplus.com
    在这里插入图片描述

    [1]. 通过容器对象进行构造
    [2]. 同上,参数为右值
    [3]. std::allocator对象作为参数
    [4]. 通过容器对象、std::allocator进行构造
    [5]. 同上,参数`ctnr`为
    [6]. 复制构造 with std::allocator
    [7]. 移动构造 with std::allocator
    

何为右值?何为移动语义?点击跳转.

int main () {
    
    
    std::deque<int> mydeck(3,100);              // deque with 3 elements
    std::list<int> mylist(2,200);               // list with 2 elements

    std::queue<int> first;                      // empty queue
    std::queue<int> second(mydeck);             // [1]. 复制构造,通过deque

    std::queue<int,std::list<int> > third;      // empty queue with list as underlying container
    std::queue<int,std::list<int> > fourth(mylist);

    std::cout << "size of first: "  << first.size()     << '\n';
    std::cout << "size of second: " << second.size()    << '\n';
    std::cout << "size of third: "  << third.size()     << '\n';
    std::cout << "size of fourth: " << fourth.size()    << '\n';

    return 0;
}

size of first: 0
size of second: 3
size of third: 0
size of fourth: 2


1.2 常用函数

   常用函数如下图,本小节按图中函数名称顺序,依次函数进行介绍:
在这里插入图片描述

  • <1>. back()返回尾元素引用。front()返回头元素引用,原型如下:
reference& back();
const_reference& back() const;

reference& front();
const_reference& front() const;

测试代码:

int main () {
    
    
    std::queue<int> myqueue;

    myqueue.push(12);
    myqueue.push(75);   // this is now the back

    myqueue.back() -= myqueue.front();

    std::cout << "myqueue.back() is now " << myqueue.back() << '\n';

    return 0;
}

myqueue.back() is now 63

  • <2>.I. emplace()在容器尾部添加元素,move。
      II. push()在容器尾部添加元素,copy/move。
      III. pop()删除容器头元素,函数原型如下:
template <class... Args> void emplace(Args&&... args);		// emplace

void push(const value_type& val);							// push
void push(value_type&& val);

void pop();													// pop

测试代码:

int main () {
    
    
    std::queue<int> myints;

    myints.push(1);
    myints.push(2);
    myints.emplace(3);    
    myints.emplace(4); 

    while (!myints.empty()){
    
    
        std::cout << myints.front() << " ";
        myints.pop();
    }

    return 0;
}

1 2 3 4

  • <3>. empty()返回容器是否为空,size()返回容器元素个数。,原型如下:
bool empty() const;
size_type size() const;

测试代码:

int main () {
    
    
    std::queue<int> myints;
    std::cout << "myints. empty: " << myints.empty() << '\n';
    std::cout << "myints. size:  " << myints.size() << '\n';

    for (int i=0; i<5; i++)
        myints.push(i);

    std::cout << "myints. empty: " << myints.empty() << '\n';
    std::cout << "myints. size:  " << myints.size() << '\n';

    return 0;
}

myints. empty: 1
myints. size: 0
myints. empty: 0
myints. size: 5

  • <4>. swap(),将本容器中的元素与参数容器中的元素互换,原型如下:
void swap (queue& x) noexcept(/*see below*/);

测试代码:

int main () {
    
    
    std::queue<int> foo,bar;
    foo.push (10);  foo.push(20); foo.push(30);
    bar.push (111); bar.push(222);

    foo.swap(bar);

    std::cout << "size of foo: " << foo.size() << '\n';
    std::cout << "size of bar: " << bar.size() << '\n';

    return 0;
}

size of foo: 2
size of bar: 3


2. std::priority_queue

2.1 简介

  std::vectorstd::deque满足priority_queue底层功能要求。默认情况下,如果没有为特定优先级队列类实例化指定容器类,则其底层实现使用std::vector

  为了始终在其内部保持堆结构(其内部默认是大顶堆),需要支持随机访问迭代器。这是由容器适配器自动完成的,在需要时自动调用算法函数make_heappush_heappop_heap

  priority_queuequeue不同点在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队。默认情况下大的元素在前。

2.2 常用函数

  • <1> . priority_queue常用函数如下图,和std::queue功能相同。
    在这里插入图片描述

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

2.3 构造函数

   stl_queue.h中有如下定义:
   template<typename _Tp,
          typename _Sequence = vector<_Tp>,
          typename _Compare = less<typename_Sequence::value_type> >
   class priority_queue { …

  ××××××××××××××××××××× 很重要 ×××××××××××××××××××××
   其中,_Tp 为数据类型,_Sequence 是容器类型(必须是用数组实现的容器,如 vector,deque等等,但不能用 list。STL里面默认用的是vector),_Compare 是比较函数( default: less<T>)。小顶堆使用std::greater<T>

  当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。如下所示:

std::priority_queue<int> pri_queue;   							// 默认大顶堆
priority_queue<int, vector<int>, less<int>> pri_queue_temp;		// 与上面这句功能相同

priority_queue<int, vector<int>, greater<int>> pri_queue_temp;	// 小顶堆


优先级的队列构造函数有五种类型,主要使用的有以下两种:

  • [1]. 无输入参数,stl_queue.h中源码如下所示。
explicit
priority_queue(const _Compare& __x = _Compare(),
 			   _Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) {
    
     
	std::make_heap(c.begin(), c.end(), comp); }

测试代码:

#include <iostream>       // std::cout
#include <queue>          // std::priority_queue
#include <vector>         // std::vector
#include <functional>     // std::greater, std::less

int main()  {
    
       
    // 以下两句相同
    std::priority_queue<int> pri_queue;
    priority_queue<int, vector<int>, less<int>> pri_queue_temp;

    pri_queue.push(1); pri_queue.push(3); pri_queue.push(2);
    
    while (!pri_queue.empty()) 
    {
    
    
        cout << pri_queue.top() << ' ';
        pri_queue.pop();
    }
    cout << endl;			// 打印: 3 2 1

    return 0;
}

  • [2]. 通过迭代器[first,last)构造对象,stl_queue.h中源码如下所示。
template<typename _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
		       const _Compare& __x = _Compare(), 
		       _Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) {
    
    
	  
	  __glibcxx_requires_valid_range(__first, __last);
	  c.insert(c.end(), __first, __last);
	  std::make_heap(c.begin(), c.end(), comp);
	}

测试代码:

#include <iostream>       // std::cout
#include <queue>          // std::priority_queue
#include <vector>         // std::vector
#include <functional>     // std::greater, std::less

int main() {
    
       
    int myints[]= {
    
    10,60,50,20};

    // 以下两句相同
    std::priority_queue<int> second(myints,myints+4);
    std::priority_queue<int, std::vector<int>, std::greater<int>> third(myints,myints+4);


    while (!second.empty()) 
    {
    
    
        cout << second.top() << ' ';
        second.pop();
    }
    cout << endl;	// 打印结果 60 50 20 10

    return 0;
}

2.4 自定义类型作为元素

  • 自定义类型作为元素 需要满足两个条件。
  • (a). 自定义类型的对象中需 重载 operator< 函数,如果是基础类型(已定义operator<),无需这一条件。
  • (b). 同时需要重写仿函数operator() 代替_Compare函数,代码如下:
class TestClass {
    
    
public:
    int x;
    TestClass(int a) {
    
    x = a;}

    // 1. 自定义类型需,运算符重载<
    bool operator<(const TestClass& a) const{
    
    
        return x < a.x;
    }
};

class Test_func {
    
    
public:
	// 2. 重写仿函数 operator()
    bool operator() (TestClass a, TestClass b) {
    
    
        return a.x < b.x;
    }
};

int main() {
    
       
    // [1]. 使用 priority_queue<TestClass>
    priority_queue<TestClass> queue_;
    queue_.emplace(TestClass(1));
    queue_.emplace(TestClass(3));
    queue_.emplace(TestClass(2));

    std::cout << "queue_: ";
    while (!queue_.empty()) 
    {
    
    
        cout << queue_.top().x;
        queue_.pop();
    }      

    // [2]. 与上述[1]相同
    priority_queue<TestClass, vector<TestClass>, Test_func> queue_temp;
    queue_temp.emplace(TestClass(1));
    queue_temp.emplace(TestClass(3));
    queue_temp.emplace(TestClass(2));
    
    std::cout << "\n queue_temp:";
    while (!queue_temp.empty()) 
    {
    
    
        cout << queue_temp.top().x;
        queue_temp.pop();
    }

    return 0;
}

queue_: 321
queue_temp:321

猜你喜欢

转载自blog.csdn.net/u013271656/article/details/113525212