除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue、priority_queue,适配器是标准库中的一个通用概念,容器、迭代器和函数都有适配器,一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。
所有容器适配器都支持的操作和类型:
size_type //一种类型,足以保存当前类型的最大对象的大小
value_type //元素类型
container_type //实现适配器的底层容器类型
A a; //创建一个名为a的空适配器
A a(c); //创建一个名为a的适配器,带有容器c的一个拷贝
关系运算符 //每个适配器都支持所有关系运算符:==、!=、<、<=、>和>=,这些运算符返回底层容器的比较结果
a.empty() //若a包含任何元素,返回false,否则返回true
a.size() //返回a中元素数目
swap(a,b) a.swap(b) //交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同
stack和queue默认是基于deque实现的,priority_queue默认是基于vector上实现的,我们可以在创建一个适配器时将一个命名的顺序容器作为第二个类型参数来重载默认容器类型
stack<string,vector<string>> str_stk; //将stack默认容器类型变为vector 在vector上实现的空栈
stack<string,vector<string>> str_stk2(svec); //str_stk2在vector上实现,初始化时保存svec的拷贝
-
stack(堆栈)
stack<类型,容器<类型>> 参数1为此stack的类型,参数2为可选参数,用于重载默认容器类型
所有适配器都要求容器具有添加和删除元素的能力,因此适配器不能构造在array之上,类似的也不能用forward_list来构造适配器,可以使用除array和forward_list之外的任何容器来构造stack,使用stack类型需要包含该头文件 #include <stack>
stack额外的操作:
stack默认基于deque实现,也可以在list或vector之上实现
s.pop() //删除栈顶元素,但不返回该元素值
s.push(item) //创建一个新元素压入栈顶,该元素通过拷贝或移动item而来
s.emplace(args) //创建一个新元素压入栈顶,该元素由args构造
s.top() //返回栈顶元素,但不将元素弹出栈
每个容器适配器都基于底层容器类型的操作定义了自己的特殊操作,我们只可以使用适配器操作,不能使用底层容器类型的操作,如添加一个元素时,必须使用适配器的push函数,而不能使用底层容器的push_back函数
-
queue(队列)
queue<类型,容器<类型>> 参数含义与stack相同
queue适配器可以构造于list或deque之上,但不能基于vector构造,因为它要求back、push_back、front、push_front,而priority_queue除了front、push_back、和pop_back操作之外还要求随机访问能力,因此它可以构造于vector或deque之上,但是不能基于list构造,这俩个适配器使用需要#include <queue>
queue的额外操作:
queue默认基于deque实现,也可以使用list,但不能使用vector实现,priority_queue默认基于vector实现,也可以用deque实现
q.pop() //出队,弹出首元素
q.front() //返回首元素,但不删除此元素,只适用于queue
q.back() //返回尾元素 只适用于queue
q.push(item) //在queue队列末尾插入一个元素,其值为item
q.emplace(args) //在queue队列末尾插入一个元素,由args构造
queue是一种先进先出的存储和访问策略,进入队列的对象被放置到队尾,而离开队列的对象则从队首删除,priority_queue允许我们为队列中的元素创建优先级,新加入的元素会排在所有优先级比它低的已有元素之前
-
priority_queue(优先队列)
priority_queue和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队,优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
priority_queue<类型,容器<类型>,排序方法> 参数1,2与其他适配器相同,参数3代表的是排序的方法,为可选参数
priority_queue的基本知识:
1.优先队列是一种特殊的队列,它能够在队列中进行排序,默认的优先级队列是最大值优先级队列,即最大的元素在队列的头部
2.优先级队列底层实现结构是vector,还可以用deque,但是不能用list
3.优先级队列使用的是堆排序
priority_queue的额外操作:
pq.pop() //弹出优先队列中最高优先级的元素
pq.top() //返回最高优先级元素,但不删除元素,只适用于priority_queue
pq.push(item) //在priority_queue的恰当位置创建一个元素,值为item
pq.emplace(args) //在恰当位置构造一个元素,有args构造
priority_queue<int> pq默认是按照值从大到小的优先级排列的
priority_queue<int, vector<int>> pq;
pq.push(1);
pq.push(100);
pq.push(134);
pq.push(31);
pq.push(214);
pq.push(71);
while (!pq.empty())
{
int value = pq.top();
pq.pop();
cout << value << " "; // 214 134 100 71 31 1
}
return 0;
控制priority_queue排序的方法:(它的排序和sort中的排序相反!)
一、使用标准库函数:
如果我们想按照int值从小到大的优先级排列的话,我们可以向函数中传入可调用对象,这样函数就可以按照我们想要的顺序进行排列,priority_queue初始化时,第三个参数就是我们要传的可调用对象,可以写成priority_queue<int,vector<int>,greater<int>> pq 来定义,而默认的是priority_queue<int,vector<int>,less<int>> pq
greater模版需要添加相关头文件#include <functional>
priority_queue<int, vector<int>,greater<int>> pq; //优先级由小到大
//priority_queue<int, vector<int>,less<int>> pq; //优先级由大到小
pq.push(1);
pq.push(100);
pq.push(134);
pq.push(31);
pq.push(214);
pq.push(71);
while (!pq.empty())
{
int value = pq.top();
pq.pop();
cout << value << " "; // 1 31 71 100 134 214
}
return 0;
二丶使用仿函数
同样,我们可以用仿函数传入第三个参数,来实现我们想要的优先级排序(越小的整数优先级越大)
struct cmp
{
bool operator() (const int& s1, const int&s2)
{
return s1 > s2; //1 4 33 55
//return s1 < s2; //55 33 4 1
}
};
int main()
{
priority_queue<int, vector<int>, cmp> pq;
pq.push(1);
pq.push(33);
pq.push(4);
pq.push(55);
while (!pq.empty())
{
int value = pq.top();
pq.pop();
cout << value << " ";
}
}
三、使用重载了<运算符的类或结构体
情况一:运算符重载函数作为成员函数(必须使用const修饰函数)
特别注意的是:没有加 const 的函数不能在 const 对象上使用,为了让函数能够在更多的情况下正常使用,则必须加const,否则编译不会通过。
class A
{
public:
A() = default;
A(int i) :id(i) {}
int id;
bool operator<(const A& another) const; //必须要加上const来修饰函数!
};
bool A::operator<(const A& another) const
{
return this->id > another.id; // 1 4 33 55
//return a1.id < a2.id; // 55 33 4 1
}
int main()
{
priority_queue<A> pq;
pq.push(A(1));
pq.push(A(33));
pq.push(A(4));
pq.push(A(55));
while (!pq.empty())
{
int value = pq.top().id;
pq.pop();
cout << value << " ";
}
}
情况二:运算符重载函数作为友元函数
class A
{
public:
A() = default;
A(int i) :id(i) {}
int id;
friend bool operator<(const A& a1,const A& a2);
};
bool operator<(const A& a1,const A& a2)
{
return a1.id > a2.id; // 1 4 33 55
//return a1.id < a2.id; // 55 33 4 1
}
int main()
{
priority_queue<A> pq;
pq.push(A(1));
pq.push(A(33));
pq.push(A(4));
pq.push(A(55));
while (!pq.empty())
{
int value = pq.top().id;
pq.pop();
cout << value << " ";
}
}
情况三:运算符重载函数作普通函数
class A
{
public:
A() = default;
A(int i) :id(i) {}
int id;
};
bool operator<(const A& a1,const A& a2)
{
return a1.id > a2.id; // 1 4 33 55
//return a1.id < a2.id; // 55 33 4 1
}
int main()
{
priority_queue<A> pq;
pq.push(A(1));
pq.push(A(33));
pq.push(A(4));
pq.push(A(55));
while (!pq.empty())
{
int value = pq.top().id;
pq.pop();
cout << value << " ";
}
}