目录
queue和priority_queue
queue称为队列,而priority_queue称为优先级队列,两者除了基础功能相同且都遵循先进先出的规则外,两个对队列内元素处理方式不同
两个队列的使用都需要头文件queue
priority_queue和queue的差别
( 此处为引子,相关知识请查看本文章的相关知识点 )
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print1(priority_queue<int> t)
{
while (!t.empty())
{
cout << t.top() << " ";
t.pop();
}
cout << endl;
}
void Print2(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
cout << "两个都按顺序插入5,1,10,2" << endl;
//设置一个队列
queue<int> q2;
//插入操作
q2.push(5);
q2.push(1);
q2.push(10);
q2.push(2);
cout << "queue:";
//上方写的打印函数
Print2(q2);
//设置一个优先队列
priority_queue<int> q1;
q1.push(5);
q1.push(1);
q1.push(10);
q1.push(2);
cout << "priority_queue:";
Print1(q1);
return 0;
}
运行结果:
可以看到,即使插入的元素以及顺序相同·,输出的元素顺序却不同,这就是优先级队列和队列的差别,优先级队列按照设定的优先级进行比较排序,而队列则不做处理
queue
queue名为队列,队列是一种容器适配器,专门设计用于在 FIFO (先进先出)中运行,其中元素插入到容器的一端并从另一端提取( 一端入,一端出 )
队列作为容器适配器实现,这些适配器是使用特定容器类的封装对象作为其基础容器的类,提供一组特定的成员函数来访问其元素。元素被推入特定容器的 “back”(队列尾) 并从其 “front” (队列头)弹出。
队列的图示:
补充知识(queue所有元素的打印):
队列无法像其他容器和数组一样直接访问中间任意下标的元素,只能访问到队列的头元素和队列的尾元素( 因为没有at函数和重载运算符 [ ] ,只有 front 和 back 函数访问头和尾 )
所以想要访问,就需要不断出队(移除队头元素)来对队头元素进行访问
代码演示:
#include <iostream>
#include <queue>
using namespace std;
int main()
{
queue<int> q;
//push为入队,在队尾加入一个元素
q.push(1);
q.push(2);
q.push(3);
//当前队列里有1,2,3这3个元素
while (!q.empty())//判断是否为空
{
//开始对队头的元素进行打印
cout << q.front() << " ";
//pop为出队,移除第一个元素
q.pop();
}
cout << endl;
//我们可以测试队列是否为空
//结果判断为空
cout << "当前q是否为空:" << q.empty() << endl;
return 0;
}
当然,上方代码存在问题,即为访问队列需要出队,这就导致了每次打印都会让队列清空,此时我们可以设置其为一个函数,并且通过拷贝的方式传给其函数,通过拷贝出来的队列进行出队打印操作将不会影响原队列
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
//当前队列里有1,2,3这3个元素
//调用写好的打印函数
Print(q);
//判断为当前队列不为空
cout << "当前q是否为空:" << q.empty() << endl;
return 0;
}
可以看到,打印后我们判断原队列是否为空是给出了0,到此我们解决了队列元素的打印问题
queue的成员函数
queue成员函数目录
- front():访问队列中的第一个元素
- back():访问队列中的最后一个元素
- push():在队列尾部插入新元素
- pop():删除队列的队头元素
- size():返回队列的大小
- swap():将容器适配器中的内容进行交换
- emplace():在队列尾添加新元素
- empty():判断队列是否为空
front()
front()函数的作用:访问队列中的第一个元素
front()函数的格式:
- value_type& front();
- const value_type& front() const;
front()的代码演示将和下面的back()代码演示一起
back()
back()函数的作用:访问队列最后一个元素
back()函数的格式:
- value_type& back();
- const value_type& back() const;
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<int> q;
q.push(10);
q.push(55);
q.push(75);
cout << "原队列:";
Print(q);
//可对其进行访问
cout << "front():" << q.front() << endl;
cout << "back():" << q.back() << endl;
//也可对其进行修改
q.back() -= q.front();
Print(q);
return 0;
}
运行结果:
push()
push()函数的作用:在队列尾部插入新元素
push()函数的格式:
- void push (const value_type& val);
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<int> q;
q.push(1);
Print(q);
q.push(2);
Print(q);
q.push(3);
Print(q);
return 0;
}
运行结果图:
pop()
pop()函数的作用:删除队列的队头元素
pop()函数的格式:
- void pop();
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << "原队列:";
Print(q);
q.pop();
cout << "pop操作后的队列:";
Print(q);
q.pop();
cout << "pop操作后的队列:";
Print(q);
return 0;
}
运行结果图:
size()
size()函数的作用:返回队列的大小
size()函数的格式:
- size_type size() const;
代码演示:
swap()
swap()函数的作用:将容器适配器中的内容进行交换
swap()函数的格式:
- void swap (queue& x) noexcept(/*see below*/);
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<int> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<int> q1, q2;
q1.push(10); q1.push(20); q1.push(30);
q2.push(1); q2.push(2);
cout << "原q1元素:";
Print(q1);
cout << "原q2元素:";
Print(q2);
q1.swap(q2);
cout << "改后q1元素:";
Print(q1);
cout << "改后q2元素:";
Print(q2);
return 0;
}
运行结果图:
emplace()
emplace()函数作用:在队列尾添加新元素( 和push一样 )
emplace()函数的格式:
- template <class... Args> void emplace (Args&&... args);
代码演示:
#include <iostream>
#include <queue>
using namespace std;
void Print(queue<string> t)
{
while (!t.empty())
{
cout << t.front() << " ";
t.pop();
}
cout << endl;
}
int main()
{
queue<string> q;
q.emplace("First");
q.emplace("Second");
q.push("Three");
cout << "队列有:";
Print(q);
cout << "队列大小有:" << q.size() << endl;
return 0;
}
运行结果图:
可以看到,emplace 和 push 的插入都是一样的
empty()
empty()函数作用:判断队列是否为空
empty()函数的格式:
- bool empty() const;
代码演示:
可以看到,我们在设置一个空队列后直接对其进行empty返回的是1,证明其为空队列
随后我们插入元素后进行empty返回的是0,证明其已经不为空
priority_queue
priority_queue称为优先级队列,优先级队列是一种容器适配器,专门设计使其第一个元素始终是它包含的元素中最大的,根据一些严格的弱排序标准
其是一个特殊的队列,类似于堆的队列,其的特殊在于用堆的特性,对队列进行自动排序
priority_queue的成员函数
priority_queue的成员函数目录
- top():返回队列头元素
- push():在队列尾部插入新元素
- pop():删除队列的队头元素
- size():返回队列的大小
- swap():将容器适配器中的内容进行交换
- emplace():在队列尾添加新元素
- empty():判断队列是否为空
priority_queue的所有函数都与普通的queue函数用法相同,只是priority_queue访问队头元素的函数改成了 top() 函数,所以不做演示
需要注意的是,priority_queue没有类似 back() 的函数,所以无法访问队列尾的元素
priority_queue的优先级设定
开头priority_queue和queue的差别时提到的priority_queue的优先级设定,这个设定决定了优先队列的排序方式,其默认为大堆(即从大到小),而我们也可以改变这个优先级的设定,让其按照需求进行排序
priority_queue的完整格式
priority_queue<int,vector<int>,less<int> >q;
- 第一个参数(int)为传入数据的类型
- 第二个参数(vector<int>)为存放的容器(需要存放在什么容器里,默认为vector)
- 第三个参数(less<int>)为优先级的设定(默认为less<int>(大堆,大到小),还有一个已经写好的设定为greater<int>(小堆,小到大),此外还可以自己设定优先级)
先来看下方案例,通过修改优先级,其排序的规则也进行了改变,输出结果也变得不同
优先级的自定义
相信你可能有疑问,明明已经给了大堆和小堆优先级的规则设定可以进行替换,为何还要自己写优先级的设定呢?
答案是当我们自定义类型且有多个参数在这类型中时,原先给的规则将不再适用
就如我们设定一个包含两个参数的自定义类型,一个表示年龄,一个表示编号,此时原先的队列规则就不知道比较年龄来排序还是比较编号排序
所以我们就需要自定义优先级的规则来给优先级队列进行排序
优先级定义样例:
//定义一个自定义类型node struct node { int x, y; //运算符重载,改变其比较大小的规则 bool operator<(const node& b)const { return this->x > b.x; } };
代码解释:
- bool operator<(const node& b)const 就是优先级规则的定义,可通过重载运算符的方式,来比较返回所需要比较的参数
- const node& b 中的 b 只是引用,可随意修改
- 除 b 可修改之外bool operator<(const node& b)const 不作修改
- return 中间对比用的对比符可以改变,其就是决定大堆和小堆的关键因素
- this 为隐藏的指针
运算符重载后就自动调用相关的优先级规则,就像下方,只需要定义相关类型的队列时便开始调用,不需要替换掉原先的规则
代码运行图(完整代码在最下方):
下面为小堆的排序,此时返回为 this->x > b.x
上面优先级定义样例时说了,return的对比符可以改变,下面代码就改变了对比符,改变为了大堆,此时返回为 this->x < b.x
补充知识:
运算符重载还可以定义在自定义类型外,只是定义在自定义类型外时,没有了隐藏的this指针,需要主动上传两个参数,如下方代码
//定义一个自定义类型node
struct node
{
int x, y;
};
bool operator<(const node& a, const node& b){
return a.x < b.x;
}
完整代码:
#include <iostream>
#include <queue>
using namespace std;
//定义一个自定义类型node
struct node
{
int x, y;
//运算符重载,改变其比较大小的规则
//在自定义类型里进行重载
bool operator<(const node& b)const {
return this->x < b.x;
}
};
//在自定义类型外进行重载
//bool operator<(const node& a, const node& b) {
// return a.x < b.x;
//}
//打印x
void Print1(priority_queue<node> t)
{
while (!t.empty())
{
cout << t.top().x << " ";
t.pop();
}
cout << endl;
}
//打印y
void Print2(priority_queue<node> t)
{
while (!t.empty())
{
cout << t.top().y << " ";
t.pop();
}
cout << endl;
}
int main()
{
priority_queue<node> q;
//自定义类型的输入
//可以是
//q.push({ x, y });
//也可以是
//node temp;
//temp.x=1;temp.y=10;
//q.push(temp);
q.push({ 1, 10 });
q.push({ 3, 20 });
q.push({ 2, 30 });
q.push({ 8, 50 });
q.push({ 5, 15 });
cout << "原先输入q的x值顺序为:1 3 2 8 5" << endl;
cout << "原先输入q的y值顺序为:10 20 30 50 15" << endl;
cout << "q的x值:";
Print1(q);//打印x
cout << "q的y值:";
Print2(q);//打印y
return 0;
}
到此,我们今天的学习就结束了,感谢观看
\\\\٩( 'ω' )و ////