栈和队列
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈和队列相关的知识,希望对你有一定的参考价值。
栈:特点:先进先出
成员函数:
stack()--构造
empty()--判空
size() --求个数
top() --返回栈顶元素
push --压栈
pop() --出栈
应用
- 最小栈创建:(可直接返回栈中最小值,即在该栈中查找最小值复杂度为O(1))
因为栈中最小值是随push和pop操作变化的,进栈时min会更新,出栈min也可能会更新,所以每个元素进栈时当前栈中最小值应该被保存下来
// 法一:
class MinStack{public :
MinStack() {
}
public void push(int x) {
int tmp = stack.top();
if(stack.isEmpty()||tmp>x){
stack.push(x);
stack.push(x);
}
else{
stack.push(x);
stack.push(tmp);
}
}
public void pop() {
s.pop();
s.pop();
}
public int top() {
return s.get(s.size()-2);
}
public int getMin() {
return s.top();
}
private:
stack<int> s;
}
//法二:在最小栈中封装两个栈,一个存元素,一个存最小值。
// push: 当存入当前元素的值是目前栈中最小值或等于最小值时,就存进去
// pop: 当出来的值等于存最小值栈中的栈顶元素时,两个一起pop出,否则只pop存元素的
class MinStack {
public:
MinStack()
{
}
void push(int x) {
svalue.push(x);
if(smin.empty()||x<=smin.top())
{
smin.push(x);
}
}
void pop() {
if(!svalue.empty())
{
if(svalue.top()==smin.top())
{
smin.pop();
}
svalue.pop();
}
}
int top() {
return svalue.top();
}
int getMin() {
return smin.top();
}
private:
stack<int> svalue;
stack<int> smin;
};
- 判断出栈顺序正不正确
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()!=popV.size())
{
return false;
}
int index=0;
int outdex=0;
stack<int> s;
while(outdex<popV.size())
{
while(s.empty()||s.top()!=popV[outdex]) //s.empty() 因为栈为空的时候s.top()非法空间不可引用
{
if(index>=pushV.size())
{
return false;
}
s.push(pushV[index]);
index++;
}
outdex++;
s.pop();
}
return true;
}
};
-
逆波兰表达式求值
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> s; int index=0; int x,y; if(tokens.size()==1) { return atoi(tokens[0].c_str()); } while(index<tokens.size()) { while(tokens[index]!="+"&&tokens[index]!="-"&&tokens[index]!="*"&&tokens[index]!="/") { s.push(atoi(tokens[index++].c_str())); } y = s.top(); s.pop(); x = s.top(); s.pop(); switch(tokens[index++].c_str()[0]) { case ‘+‘:s.push(x+y);break; case ‘-‘:s.push(x-y);break; case ‘*‘:s.push(x*y);break; case ‘/‘:{ if(y!=0) s.push(x/y); else s.push(0); break; } } } return s.top(); } };
在树的遍历中,也会用到栈,一定要练题!
队列
特点:先进后出
成员函数:
queue()--构造
empty()--判空
size() --求个数
front()--返回队头元素
back() --返回队尾元素
push --压栈
pop() --出栈
在此介绍一种很重要的队列叫做优先队列
优先队列
按照堆的顺序存储元素,默认是通过vector来存储的
他所用的函数模板是:
template <class T,class Container=vector<T>,class Compare=less<T>)
说明:1.在默认的情况下,创建的是大堆顺序存储,用的是less方法的比较
创建对象时,可通过传greater对象来创建一个小堆的顺序
//例:
priority_queue<int> q1;
priority_queue<int,vector<int>,less<int>> q2;
//q1和q2创建出来都是以vector容器,大根堆顺序 存储
priority_queue<int,deque<int>,greater<int>> q3;
//q3创建出来是以deque容器,小根堆顺序 存储
//根据形参赋值规则,要从头开始赋值,所以要串时参时,传比较方法一定要带上承载容器
vector<int> v{ 2, 5, 3, 8, 9, 0, 1 };
priority_queue<int> q3(v.begin(), v.end());
2.如果在优先级队列中放自定义数据类型,用户需要在自定义类型中提供>或 < 的重载
//例:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d)const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
/*bool operator<(const Date* d)const
{
return (_year < d->_year) ||
(_year == d->_year && _month < d->_month) ||
(_year == d->_year && _month == d->_month && _day < d->_day);
}
bool operator>(const Date* d)const
{
return (_year > d->_year) ||
(_year == d->_year && _month > d->_month) ||
(_year == d->_year && _month == d->_month && _day > d->_day);
}*/
friend ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
class Less
{
public:
bool operator()(const Date* pLeft, const Date* pRight){
return *pLeft < *pRight;
}
};
void Test(){
Date d1(2019, 10, 21);
Date d2(2019, 10, 20);
Date d3(2019, 10, 22);
priority_queue<Date> q1;
q1.push(d1);
q1.push(d2);
q1.push(d3);
//对于指针,如果不提供比较方法,会用地址大小来使用堆排序
//如果想按照指针所指向空间里存放的元素来比较,也要自己提供比较方式
//因而,要按照自己的需求排序时,都要自己提供比较方式才能达到目的
//如下文中的Less方法
priority_queue<Date*, vector<Date*>, Less> q2;
q2.push(&d1);
q2.push(&d2);
q2.push(&d3);
}
提出一个重要概念:
容器适配器:
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该中模式是将一个类的接口转换成客户希望的另外一个接口。
在类中具体实现方式:一个类在底层通过将其他容器进行封装,通过提供不同的函数接口,以达到不同的功能。
stack、queue、priority_queue 都是容器适配器,因为在STL中,每个容器都有自己的实现方式,而他们只是通过对其他容器进行封装而成,因为是容器适配器,而并不是容器
stack、queue、priority_queue底层的大概实现方式
namespace MyStack{
template<class T,class Container=deque<T>> //默认用栈承载
class stack{
public:
stack(){
}
void push(const T &value){
q.push_back(value);
}
void pop(){
q.pop_back();
}
T& top(){
return q.back();
}
const T& top() const
{
return q.back();
}
size_t size(){
return q.size();
}
bool empty(){
return q.empty();
}
private:
Container q;
};
}
void Test2()
{
MyStack::stack<int,list<int>> s1;
s1.push(1);
s1.push(2);
s1.push(3);
s1.pop();
cout << s1.size() << endl;
cout << s1.empty() << endl;
}
namespace MyQueue{
template<class T, class Container = deque<T>>
class queue{
public:
queue(){
}
void push(const T &value){
q.push_back(value);
}
void pop(){
q.pop_front();
}
T& front(){
return q.front();
}
const T& front() const
{
return q.front();
}
T& back()
{
return q.back();
}
const T& back() const
{
return q.back();
}
size_t size(){
return q.size();
}
bool empty(){
return q.empty();
}
private:
Container q; 、
//用的是一个其他容器,在该类中提共其他方法在进行封装就成了栈
};
}
void Test3()
{
MyQueue::queue<int,list<int>> q1;
q1.push(1);
q1.push(2);
q1.push(3);
q1.pop();
cout << q1.size() << endl;
cout << q1.empty() << endl;
}
为什么stack和queue使用的是deque而不是vector?
因为对于deque来说,扩容是很容易的,只是将指针改变一下,而vercot又得重新将元素搬来搬去效率很低。此外,栈和队列不会去遍历(不提供迭代器),这个特性又把deque的弊端给忽略了。
不用list是因为list的空间利用率不高
namespace MyPriorityqueue{
template <class T,class Contain=vector<T>,class Compare=less<T>>
class priority_queue{
public:
priority_queue()
:_con()
{
}
template <class Iterator>
priority_queue(Iterator start, Iterator end)
:_con(start, end)
{
for (int i =( _con.size()-2)/2; i >=0; i--)
{
AdjustDown(i);
}
}
void size(){
return _con.size();
}
bool empty(){
return _con.empty();
}
void push(const T& value){
_con.push_back(value);
AdjustUP(_con.size() - 1);
}
void pop(){
swap(_con.front(), _con.back());
_con.pop_back();
AdjustDown(0);
}
private:
void AdjustDown(int parent){
int child = parent * 2 + 1;
while (child<_con.size())
{
if (child+1<_con.size()&&_comp(_con[child], _con[child + 1]))
{
child += 1;
}
if (_comp(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void AdjustUP(int child)
{
int parent = (child - 1) / 2;
while (child>0){
if (_comp(_con[parent],_con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else{
return;
}
}
}
private:
Contain _con;
Compare _comp;
};
}
void Test4(){
deque<int> dq{ 1, 2, 3 ,5,6,2};
MyPriorityqueue::priority_queue<int,deque<int>,greater<int>> p1(dq.begin(),dq.end());
p1.pop();
p1.push(0);
}
以上是关于栈和队列的主要内容,如果未能解决你的问题,请参考以下文章