一、stack的介绍和使用
1、stack的介绍
1、stack是一种容器适配器,专门用于具有LIFO(后进先出)的上下问环境中,其删除只能从容器的一端进行,元素的插入和提取操作;
2、stack时作为容器适配器被动实现的,容器适配器即使对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(栈顶)被压入或者弹出;
3、stack的底层容器可是以任何标准的容器类模板或者一些其他特定的容器类,这些容器应该支持以下操作:
- empty:判空操作
- back:获取尾部元素操作
- push_back:尾部插入元素
- pop_back:尾部删除元素
4、标准容器vector 、 deque 、list 均符合这些要求,默认情况下,如果没有为stack 指定特定的底层容器,默认使用 deque;
2、stack的使用
函数说明 | 接口说明 |
stack(const container_typr& ctnr = container_typr()) | 构造空的栈 |
bool empty() const | 检测stack是否为空 |
size_type size() const | 返回stack中元素的个数 |
value_type& top() | 返回栈顶的元素 |
const value_type& top() const | 返回栈顶元素的const引用 |
void push(const value_type& val) | 将元素val压栈 |
void pop() | 将stack中尾部元素弹出 |
tempalce<class... Args> void empale(Args&&... args) | 将stack的栈顶构造元素 |
void swap(stack& x) | 交换两个栈中的元素 |
二、queue的介绍和使用
1、queue的介绍
1、队列是一种容器配置器,专门用于在FIFO(先入先出)中操作,其中从容器的一段进入元素,另一端提取元素;
2、队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问元素,元素从队尾进入,从队头出队列;
3、底层容器可以是标准容器类模板之一,该底层容器至少支持以下操作:
- empty:判断队列是否为空
- size:返回队列中有效元素的个数
- front:返回队头元素的引用
- back:返回队尾元素的引用
- push_back:在队列尾部入队列
- pop_front:在队列头部出队列
4、标准容器 deque 和 list 都满足了要求,但是默认情况下使用 deque;
2、queue的使用
函数声明 | 接口说明 |
queue(const container_type& ctnr = container_type()) | 构造空的队列 |
bool empty() | 检测队列是否为空, |
size_type size() cosnt | 返回队列中的有效元素个数 |
value_type& front() | 返回队头元素的引用 |
const value_type& front() const | 返回队头元素的const 引用 |
value_type& back() | 返回队尾元素的引用 |
const value_type& back() const | 返回队尾元素的const 引用 |
void push(value_type& val) | 在队尾插入 val元素 |
void pop() | 将队头元素出列 |
template<class... Args> void emplace(Args&&... args) | 在队尾构造元素 |
void swap(queue& x) | 交换两个队列中的元素 |
三、priority_queue是介绍和使用
1、priority_queue的介绍
1、优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的;
2、类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素);
3、优先队列被实现为容器适配器;
4、stack的底层容器可是以任何标准的容器类模板或者一些其他特定的容器类,这些容器应该支持以下操作:
- empty:判空操作
- back:获取尾部元素操作
- push_back:尾部插入元素
- pop_back:尾部删除元素
5、标准容器类 vector 和 deque 都可以用来是先priority_queue,默认情况下使用vector;
6、需要支持随机访问迭代器,以便于时钟在内部保持堆结构,容器适配器通过在需要时自动调用算法函数 make_heap、push_heap、pop_heap来自动完成操作;
2、priority_queue的使用
优先级队列默认使用 vector 作为其底层存储数据的容器,在vector 上有使用了堆算法将 vector 中元素构造成堆的结构,因此 priority_queue就是被认为时堆,所有需要用到堆的地方都可以使用priority_queue来替代。(默认是大堆结构)
函数声明 | 接口说明 |
priority_queue(const Compare& x= Compare(),const Container& y = Container()) | 构造一个空的优先级队列 |
template priority_queue(Inputlterator first,Inputlterator last,const Compare& comp = Compare(),const Container& ctnr = Container()) | 用[fist,last)区间构造优先级队列 |
bool empty() const | 判空 |
const value_type& top() const | 返回优先级中最大的元素 |
void push(const T& x) | 插入x |
void pop() | 删除对顶元素 |
3、pirority_queue的是使用注意点:
1、默认情况下,priority_queue是大堆;
void Test1() {
//默认使用大堆结构
vector<int> v{3, 2, 7, 6, 0, 4, 1, 9, 8, 5};
priority_queue<int> q1;
for (auto x : v)
q1.push(x);
cout << q1.top() << endl;
//如果需要小堆,将第三个参数模板换成 greater 的比较方式
priority_queue<int, vector<int>, greater<int>> q2(v.begin(),v.end());
cout << q2.top << endl;
}
2、如果在 priority_queue 中放自定义类型的数据,用户需要在自定义类型中提供 < or > 的重载;
class Date {
public:
Date(int year = 1900, int mouth = 1, int day = 1)
:_year(year)
, _mouth(mouth)
, _day(day)
{}
bool operator<(const Date& d) const {
return (_year < d._year) ||
(_year == d._year && _mouth < d._mouth) ||
(_year == d._year && _mouth == d._mouth && _day < d._day);
}
bool operator>(const Date& d)const {
return (_year > d._year) ||
(_year == d._year && _mouth > d._mouth) ||
(_year == d._year && _mouth == d._mouth && _day > d._day);
}
friend ostream& operator<<(ostream&& _cout, const Date& d) {
_cout << d._year << "-" << d._mouth << "-" << d._day;
return _cout;
}
private:
int _year;
int _mouth;
int _day;
};
void Test2(){
//大堆
priority_queue<Date> q1;
q1.push(Date(2018, 1, 1));
q1.push(Date(2018, 2, 2));
q1.push(Date(2018, 3, 3));
//小堆
priority_queue<Date, vector<Date>, greater<Date>> q2;
q2.push(Date(2017, 1, 1));
q2.push(Date(2017, 2, 2));
q2.push(Date(2017, 3, 3));
}
3、有些情况下,用户可能需要提供比较器规则;
class Less {
public:
bool operator()(const Date* pLeft,const Date* pRight) {
return *pLeft < *pRight;
}
};
void Test3() {
priority_queue<Date*, vector<Date*>, Less> q;
q.push(&Date(2018, 1, 1));
q.push(&Date(2018, 2, 2));
q.push(&Date(2018, 3, 3));
}
四、模拟实现 stack 和 queue
1、stack的模拟实现(deque)
namespace My {
template<class T,class Con = deque<T>>
class Stack {
public:
Stack(){}
void push(const T& x) {
_c.push_back(x);
}
void pop() {
_c.pop_back();
}
T& Top{
return _c.back();
}
const T& Top() const {
return _c.back();
}
size_t Size() const {
return _c.size();
}
bool Empty() const {
return _c.empty();
}
private:
Con _c;
};
}
2、queue的模拟实现(deque)
namespace My {
template<class T,class Con = deque<T>>
class Queue {
public:
Queue(){}
bool Empty() const {
return _c.empty();
}
size_t Size() const {
return _c.size();
}
T& Front() {
return _c.front();
}
const T& Front() cosnt {
return _c.front();
}
T& back() {
return _c.back();
}
const T& back() cosnt {
return _c.back();
}
void Pop() {
_c.pop_front();
}
void Push(const T& x) {
_c.push_back(x);
}
private:
Con _c;
};
}
3、为什么选择deque作为 stack 和 queue 的底层默认容器(常见面试题)
stack 是一种 LIFO 的特殊线性结构,因此只要具有 push_back 和 pop-back 操作的线性结构,都可以作为 stack 的底层容器;queue 是一种 FIFO 的特殊线性结构,只要具有 push_back 和 pop_front 操作的线性结构都可以;
但是STL中对 stack 和 queue 的底层默认选用 deque 的主要原因是:
- stack 和 queue 不需要遍历(因此 stack 和 queue 没有迭代器),只需要在固定的一端或两端进行操作即可;
- 在 stack 和 queue 中元素增长时,deque 比 vector 的效率高;queue 中的元素增长时,deque 不仅效率高,而且内存使用率高;