1. queue(队列)
1.1 queue的底层实现
- queue的底层实现是线性表,可以用数组(如循环数组)和链表来实现,队列元素是FIFO的,即先进先出。
1.2 循环队列
1. 循环队列通过 front 和 rear 来标记队首和队尾。如果循环队列用数组实现,并记数组长度为len,那么:
- 出队:front = (front + 1)% len
- 入队:rear = (rear + 1) % len
2. 判断队列是否为空:front == rear
3. 判断循环队列是否为满
- 增加一个变量:Size来标记队列元素或者bool flag值来标记最后一次操作是出队还是进队
- 判断队列为满时: rear == front && size== lenh or rear == front && flag = 进队(比如是true)
- 牺牲一个空间: 判断队列为满时条件:(rear+1)% len == front
1.3 queue 基本操作
#include <queue> //头文件
queue<int> q;
q.push(); // 插入元素到队尾 (并排序)
q.pop(); // 弹出队头元素
q.front(); //普通队列访问队头元素
q.empty(); // 队列是否为空,为空返回1,不为空返回0
q.size(); // 返回队列内元素个数
q.emplace(); // 原地构造一个元素并插入队列(C++11新特性)
q.swap(); // 交换内容
priority_queue<int> pq;
pq.top(); //优先级队列访问队头元素,因为优先级队列的底层实现是堆
2. priority_queue(优先级队列、最大堆、最小堆)
2.1 priority_queue和queue的区别(
优先队列和普通队列的区别)
- 用优先级队列声明最大堆和最小堆,需要添加头文件:#include<functional>
- 优先级队列的头文件:#include <queue>
- 在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除,队头用
q.top()
而不是q.front()。
priority_queue
可以自定义其中数据的优先级,让优先级高的排在队列前面,优先出队。- 优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
- 在STL里有priority_queue,实现优先队列的结构。在优先队列中,优先级高的元素先出队列。
2.2 定义:priority_queue<Type, Container, Functional>
- Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,不能用 list。STL里面默认用的是vector),Functional 是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆(最大堆)。
- 注意:priority_queue中的三个参数,后2个可以省去,因为有默认参数,不过若有第三个参数,必定要写第二个参数。
-
第一种,直接使用默认的,默认是最大堆:
它的模板声明带有三个参数,priority_queue<Type, Container, Functional>
Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
Container 必须是用数组实现的容器,比如 vector、deque ,但不能用 list。
STL里面默认用的是 vector,比较方式默认用 operator< ,所以如果你把后面俩个
参数缺省的话,优先队列就是大顶堆,队头元素最大。
#include<queue>
#include<functional>
//降序队列:最大堆
priority_queue <int,vector<int>,less<int> > q; //这里c++11之前需要空格,不然成了右移运算符,现在可以不留空格了
//上面等价于:
priority_queue <int> q;//默认为最大堆
//-----------------------
int a[len] = {3,5,9,6,2};
priority_queue<int> qi;
for(i = 0; i < len; i++)
qi.push(a[i]);
for(i = 0; i < len; i++)
{
cout<<qi.top()<<" ";
qi.pop();
}
通过<操作符可知在整数中元素大的优先级高,故上例中输出结果为:9 6 5 3 2
-
第二种,显示声明为最小堆:
如果要用到小顶堆,则一般要把模板的三个参数都带进去。要把元素从小到大输出,可传入一个比较函数,使用functional.h函数对象作为比较函数。
STL里面定义了一个仿函数 greater<>,对于基本类型可以用这个仿函数声明小顶堆
priority_queue<int, vector<int>, greater<int> > qi2;对于自定义类型,必须自己重载 operator< 或者自己写仿函数!!!
//------------------
#include <iostream>
#include<queue>
#include<functional>
//升序队列:最小堆
priority_queue <int,vector<int>,greater<int> > q; //这里一定要有空格,不然成了右移运算符
using namespace std;
struct Node{
int x, y;
Node( int a= 0, int b= 0 ):
x(a), y(b) {}
};
bool operator<( Node a, Node b ){
if( a.x== b.x )
return a.y> b.y;
return a.x> b.x;
}
int main(){
priority_queue<Node> q;
for( int i= 0; i< 10; ++i )
q.push( Node( rand(), rand() ) );
while( !q.empty() ){
cout << q.top().x << ' ' << q.top().y << endl;
q.pop();
}
getchar();
return 0;
}
或者这样定义也是能达到效果的:
struct Node{
int x, y;
Node( int a= 0, int b= 0 ):
x(a), y(b) {}
friend operator<( Node a, Node b ){
if( a.x== b.x )
return a.y> b.y;
return a.x> b.x;
}
};
//-------------------
自定义类型重载 operator< 后,声明对象时就可以只带一个模板参数。
但此时不能像基本类型这样声明
priority_queue<Node, vector<Node>, greater<Node> >;
原因是 greater<Node> 没有定义,如果想用这种方法定义
则可以按如下方式
例子:
#include <iostream>
#include <queue>
using namespace std;
struct Node{
int x, y;
Node( int a= 0, int b= 0 ):
x(a), y(b) {}
};
struct cmp{
bool operator() ( Node a, Node b ){
if( a.x== b.x ) return a.y> b.y;
return a.x> b.x; }
};
int main(){
priority_queue<Node, vector<Node>, cmp> q;
for( int i= 0; i< 10; ++i )
q.push( Node( rand(), rand() ) );
while( !q.empty() ){
cout << q.top().x << ' ' << q.top().y << endl;
q.pop();
}
getchar();
return 0;
}
2.3 基本操作函数
-
priority_queue 也实现了赋值运算,可以将右操作数的元素赋给左操作数;同时也定义了拷贝和移动版的赋值运算符。需要注意的是,priority_queue 容器并没有定义比较运算符。因为需要保持元素的顺序,所以添加元素通常会很慢。
#include <queue> //头文件
priority_queue<int> pq;//默认最大堆
pq.push(); // 插入元素到队尾 (并排序)
pq.pop(); // 弹出队头元素,即移除第一个元素
pq.top(); //优先级队列访问队头元素,返回优先级队列中第一个元素的引用,因为优先级队列的底层实现是堆
pq.empty(); // 队列是否为空,为空返回1,不为空返回0
pq.size(); // 返回队列内元素个数
pq.emplace(); // 原地构造一个元素并插入队列(C++11新特性)
pq.swap(); // 交换内容
//对 priority_queue;//进行操作的一些限制:
push(const T& obj);//将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
push(T&& obj);//将obj放到容器的适当位置,这通常会包含一个排序操作。
emplace(T constructor a rgs...);//通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
swap(priority_queue<T>& other);//和参数的元素进行交换,所包含对象的类型必须相同。
//---------------------
queue<int> q;
q.front(); //普通队列访问队头元素
2.4 队列元素是普通int
- greater 和 less 是 std 实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现是类中实现的一个operator(),这个类有类似函数的行为,是一个仿函数类)。
//降序队列:最大堆
priority_queue <int,vector<int>,less<int> > q; //这里一定要有空格,不然成了右移运算符
//等价于:
priority_queue <int> q;
//升序队列:最小堆
priority_queue <int,vector<int>,greater<int> > q; //这里一定要有空格,不然成了右移运算符
2.5 队列元素是pair<int,int>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
priority_queue<pair<int, int> > pq;
//下面三种添加方式是一样的:
//1.第一种添加pair元素方式
pair<int, int> i1(1, 2);
pq.push(i1);
//2.第一种添加pair元素方式
pq.push(pair<int, int>(1, 3));
//3.第一种添加pair元素方式
pq.push(make_pair(2,5);
while (!a.empty()) //该优先级队列不为空
{
//这里访问元素用:pq.top().first, 和map中有区别,用.不用->
cout<<pq.top().first<<' '<<pq.top().second<<'\n';
pq.pop();
}
//------------------------
//在map中访问元素的方式:
map<int,int> m;
m.insert(pair<int,int>(8,9));
cout<<endl;
cout<<m->first<<' '<<m->second<<'\n';//用->
}
输出:
2 5
1 3
1 2
8 9
2.6 队列元素是自定义类型
- 对于自定义类型,则必须自己重载 operator< 或者自己写仿函数
#include <iostream>
#include <queue>
using namespace std;
//方法1
struct tmp1 //重载运算符<
{
int x;
tmp1(int v) {x = v;}
bool operator<(const tmp1& t) const
{
return x < t.x; //大顶堆
}
};
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty()) //队列非空
{
cout<<d.top().x<<'\n';
d.pop();
}
cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(c);
f.push(b);
f.push(a);
while (!f.empty())
{
cout<<f.top().x<<'\n';
f.pop();
}
}
输出:
3
2
1
3
2
1
3. deque(双端队列)
3.1 底层实现
- C++ STL容器deque和vector类似,采用动态数组来管理元素。
- #include <deque> //头文件
- 是定义在命名空间std内的一个class template
- template<class _Ty, class _Ax = allocator<_Ty> > class deque; 第一个template参数用来表示元素型别,第二个可有可无,指定内存模型。一般使用默认的内存模型。
- 与vector不同的是deque的动态数组首尾都开放,因此能够在首尾进行快速地插入和删除操作。
3.2 deque的内部结构:
- deque是一种优化了的对序列两端元素进行添加和删除操作的基本序列容器。
- deque通常由一些独立的区块组成,第一区块朝某方向扩展,最后一个区块朝另一方向扩展。
- deque允许较为快速地随机访问,但deque不像vector一样把所有对象保存在一个连续的内存块,而是多个连续的内存块,并且在一个映射结构中保存对这些块以及顺序的跟踪。
3.3 deque的特点
- 支持随机访问,即支持[]以及at(),但是性能没有vector好。
- 可以在内部进行插入和删除操作,但性能不及list。
3.4 deque和vector的区别
- 1、deque两端都能够快速插入和删除元素。vector只能在尾端进行。
- 2、deque的元素存取和迭代器操作会稍微慢一些,因为deque的内部结构会多一个间接过程。
- 3、迭代器是特殊的智能指针,而不是一般指针。迭代器需要在不同的区块之间跳转。
- 4、deque可以包含更多的元素,其max_size可能更大。因为不止使用一块内存。
- 5、不支持对容量和内存分配时机的控制。注意:在除了首尾两端的其他地方插入和删除元素,都将会导致指向deque元素的任何pointers、references、iterators失效。不过,deque的内存重分配优于vector,因为其内部结构显示不需要复制所有元素。
- 6、deque的内存区块不再被使用时,会被释放。deque的内存大小可缩减。不过,视具体情况而定。
3.5 deque和vector的相似性
- 1、在中间部分插入和删除元素相对较慢,因为所有元素都要被移动。
- 2、迭代器属于随机(?)存取迭代器。
3.6 deque适用的场景
- 1、需要在两端插入和删除元素。
- 2、无需引用容器内的元素。
- 3、要求容器释放不再使用的元素。
3.7 deque相关操作函数
deque的各项操作只有下面2点和vector不同,其他均与vector相同:
- deque不提供容量操作:capacity()和reverse()。
- deque直接提供函数完成首尾元素的插入和删除。
注意:
- 1、除了at()函数,其他成员函数都不会检查索引或迭代器是否有效。
- 2、元素的插入和删除可能会导致内存重新分配。所以任何插入或删除操作都会使所有指向deque元素的pointers、reference、iterators失效。唯一例外的是在首尾插入元素之后,pointers和reference可能仍然有效。
1. 构造函数和析构函数
#include<deque>
deque<int> dq;
deque<int> dq1;
deque<int> dq2(dq1);
deque<int> dq(n);//n个元素空间
deque<int> dq(n,2);//n个2的双端队列
deque<int> dq(beg,end);//dq的(beg,end)范围内的元素
dq.~deque<int>;//销毁dq中所有元素并释放内存空间
2. 静态操作
deque<int> dq;
dq.size();
dq.empty();
dq.max_size;
dq.at[idx];//返回下标是idx的元素
dq.front();
dq.back();
dq.begin();
dq.end();
dq.rbegin();
dq.rend();
3. 比较两个双端队列
deque<int> dq1;
deque<int> dq2;
dq1==dq2
dq1!=dq2
dq1<dq2
dq1>dq2
dq1<=dq2
dq1>=dq2
4. 动态操作
deque<int> dq1;
deque<int> dq2;
dq1=dq2;
dq1.assign(n,elem);
dq1.assign(beg,end);
dq1.swap(dq2);
swap(dq1,dq2);
deque<int> dq;
dq.insert(pos,elem);
dq.insert(pos,n,elem);
dq.insert(pos,beg,end);
dq.push_back(elem);
dq.pop_back();
dq.push_front(elem);
dq.pop_front();
dq.erase(pos);
dq.erase(beg,end);
dq.resize(num);//如果num>dq.size(),新的元素空间用默认值初始化
dq.resize(num,elem);//如果num>dq.size(),新的元素空间用elem初始化
dq.clear();//移除所有元素,使容器为空,不释放空间
参考:https://blog.csdn.net/weixin_36888577/article/details/79937886
参考:https://www.jianshu.com/p/978311125b34
参考:https://blog.csdn.net/xiajun07061225/article/details/7442816