【C/C++】队列、优先队列(含结构体重载)、双向队列

1,398 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情


队列(queue)

特点: 先进先出 (和我们日常生活中的排队是一样的)。

使用规则

  • 头文件 #include <queue>
  • 队列的声明( que 为对象,名字自定义):
    • queue<int> que;
    • queue<string> que;
    • queue<node> que;
  • 队列常用操作
    • que.front():返回 queue 中第一个元素的引用。如果 queue 为空,返回值是未定义的。
    • que.back():返回 queue 中最后一个元素的引用。如果 queue 为空,返回值是未定义的。
    • que.push():在 queue 的尾部添加一个元素。
    • que.pop():删除 queue 中的第一个元素。
    • que.size():返回 queue 中元素的个数。
    • que.empty():判断队列是否为空,如果 queue 中没有元素的话,返回 true。

小结

在学习优先队列和双向队列之前,需要掌握队列的使用方法。因为他们彼此之间的使用规则都比较类似,而队列作为他们中最简单的数据结构,所以放在开头学习。

优先队列(priority_queue)

特点:队列中的元素按照优先级顺序进行排序。(在队列的基础上增加了优先级顺序)

时间复杂度: 插入和删除操作的时间复杂度为 O(log2n)O(log_2n)

n 次插入删除操作的时间复杂度为 O(nlog2n)O(n*log_2n)

使用规则

  • 头文件 #include <queue>
  • 优先队列的声明( que 为对象,名字自定义):
    • priority_queue<node> que; //基础类型,默认是大顶堆
    • priority_queue<int, vector<int>, less<int> > que; //大顶堆,大元素在上
    • priority_queue<int, vector<int>, greater<int> > que; //小顶堆,小元素在上

需要注意的是优先队列的声明:

  • less 翻译为较少的,但是在优先队列的声明中表示大顶堆
  • greater 翻译为更大的,但是在优先队列的声明中表示小顶堆
  • 优先队列常用操作
    • que.top() :访问队头元素。

    优先队列中访问头元素用top()方法,对比普通队列中用的是front()方法。

    • que.push(x):将元素 x 放入队列。

    优先队列会将放入的元素自动根据优先级进行排序。

    • que.pop():将队首元素弹出(删除)。
    • que.size():返回队列大小(也就是队列中的元素个数)。
    • que.empty():判断队列是否为空,如果队列为空,返回 true

结构体重载

对结构体 struct 元素进行自定义优先级。当我们将结构体元素放入优先队列或者 sort() 函数中进行排序时需要对结构体元素赋予一定的优先级顺序,这时候就可以在结构体元素中自定义重载,也就是让结构体遵从结构体内某元素的优先级进行排序。

结构体重载定义的模板

struct node{
        int u, v;
        friend bool operator < (node a , node b){
            if(a.u == b.u) return a.v > b.v;
            return a.u > b.u; 
            //优先队列中的排序大小与结构体里重载的符号相反
            //sort()函数中的排序大小与结构体里重载的符号一致
        }
};

这里需要注意 return 语句中的 >< 符号:

  • 优先队列中的优先级大小与结构体里重载的符号相反(> 符号表示小顶堆,按照从小到大排序)
  • sort()函数中的排序大小与结构体里重载的符号一致(> 符号表示按照从大到小排序)

声明和使用

  • 结构体重载的优先队列声明方式:priority_queue<node> que;

只需要按照优先队列的声明方式即可,因为在 node 结构体里以及自定义重载了结构体元素的排序优先级。

  • sort() 函数对结构体数组进行排序:sort(node, node + n);node 表示开始的位置,node + n 表示结束的位置,n 表示结构体数组大小,这里表示对 node[0]node[n - 1] 进行排序)

注意 sort() 函数需要包含头文件 #include <algorithm>
sort() 函数排序的时间复杂度为 O(nlog2n)O(n*log_2n)
sort() 函数还可以使用 cmp 传参进行自定义排序优先级:sort(node, node + n, cmp);

static bool cmp(node a, node b){
    if(a.u == b.u) return a.v > b.v;
    return a.u > b.u;
}	 

自定义 sort() 函数中的 cmp 需要加 static 声明。在 class 类中的 sort() 函数的参数 cmp,定义时要声明为 staticstatic 静态成员函数不用加对象名,就能直接访问函数(这也是静态成员函数的一大优点)

sort() 函数使用标准库函数进行定义排序顺序:
升序(默认):sort(begin,end,less<data-type>())
降序:sort(begin,end,greater<data-type>())

双向队列(deque)

在上述的队列数据结构中我们只能对队首元素进行弹出(删除)操作,对队尾进行插入(添加)操作,如果我们想对队首进行插入或者队尾进行删除操作的话,队列时无法完成的,所以有了双向队列,在双向队列中,可以对双向队列的头部和尾部进行插入删除操作

时间复杂度: 插入和删除操作的时间复杂度为 O(1)O(1)

n 次插入删除操作的时间复杂度为 O(n)O(n)

使用规则

  • 头文件 #include <deque>

  • 优先队列的声明( deq 为对象,名字自定义):

    • deque<int> deq;
    • deque<double> deq;
    • deque<node> deq;
  • 双向队列常用操作:

    • deq.back():访问队尾元素。
    • deq.front():访问队首元素。
    • deq.clear():清空双向队列。

    这里对比C++中的队列 queue 自身是不支持 clear 操作的,但是双端队列 deque 是支持 clear 操作的。那么要清空队列 queue 我们只能通过 while 循环来清空队列。

    queue<int> que;//头文件 #include <queue>
    deque<int> deq;//头文件 #include <deque>
    while(que.size()) que.pop();//清空队列que
    deq.clear();//清空双向队列
    
    • deq.size():返回双向队列的大小(也就是双向队列中元素的个数)
    • deq.empty():判断双向队列是否为空,为空返回 true
    • deq.push_front(x):在双向队列头部插入元素 x
    • deq.push_back(x):在双向队列尾部插入元素 x
    • deq.pop_front():弹出(删除)双向队列头部元素。
    • deq.pop_back():弹出(删除)双向队列尾部元素。
    • deq.resize(x):改变双向队列的大小,也就使得队列中元素个数为 x

总结

以上就是队列、优先队列和双向队列的数据结构的介绍和总结,其中在介绍优先队列时加入了结构体重载的介绍。

使用时需要注意对应的队列操作时间复杂度,结构体重载的用法常常用在 sort 函数和优先队列中,往往需要对结构体中某个成员变量进行排序时用到。


结束语

生活是讲究节奏的。该奋起直追时,就全力以赴;该养精蓄锐时,就从容修养。流流汗,休息时更加惬意;歇歇脚,再出发精神百倍。