文章目录
(一)基本介绍
在此之前我们我们用C语言实现过栈,栈的实现
对于栈的底层不熟悉的话,可以看看这篇文章
最主要的特点就是遵循 Last In First Out(LIFO)
1、基本概念
接下来,我们先从文档来认识,看文档中是如何描述的
-
从上我们看出stack是STL库中的一种容器,它用于存储数据,并遵循Last In First Out(LIFO)的规则;
-
堆栈是一种容器适配器,stack在C++ STL库中实现为一个模板类,提供一组特定的成员函数来访问其元素
2、容器适配器
- 容器适配器(又叫配机器)是STL库中的一类容器,使用已有的容器类来实现适配器的功能
- 简单来说,就是将通过封装某个序列式容器(如vector、deque和list等),并重新组合该容器中包含的成员函数,从而达到某种需求
容器适配器包括三种:stack
、queue
和priority_queue
容器适配器有以下特点
-
- 使用现有容器类作为底层实现
-
- 基于已有的功能来添加和删除元素(例如push()、pop()、top()等
-
- 通常有所限制,可优化
总体来说,容器适配器是为了方便使用STL当中已有容器类设计的,通过简洁的接口,简化某些数据结构的实现
(二)基本使用
void test()
{
stack<int> st;
// 在栈顶添加元素
st.push(1);
st.push(2);
st.push(3);
st.push(4);
st.push(5);
//栈顶元素
if (!st.empty())
cout << st.top() << " " << st.size() << endl;
// 从栈顶弹出元素
st.pop();
// 获取栈顶元素
cout << st.top() << endl;
// 检查栈是否为空
if (st.empty())
cout << "Stack is empty" << endl;
else
cout << "Stack is not empty" << endl;
// 获取栈的大小
cout << "Stack size is " << st.size() << endl;
}
- stack是一个
模板类
,需要指定对应的数据类型
(三)stack模拟实现
stack的底层容器可以是任何标准的容器类模板
或者一些其他特定的容器类
这些容器类应该支持以下 操作:
- empty:判空操作
- back:获取尾部元素操作
- push_back:尾部插入元素操作
- pop_back:尾部删除元素操作
标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况=下使用deque
stack成员函数
stack模拟实现
对于stack来说,我们可以使用vector,list和deque来实现,我们默认使用deque
- 1️⃣push()
void push(const T& x)
{
_con.push_back(x);
}
- 2️⃣pop()
void pop()
{
_con.pop_back();
}
- 3️⃣top()
const T& top()
{
return _con.back();
}
- 4️⃣empty()
bool empty()
{
return _con.empty();
}
- 5️⃣size()
size_t size()
{
return _con.size();
}
完整代码如下
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
using namespace std;
namespace mystack
{
template<class T, class Container = deque<T>>
class stack
{
public:
void push(const T& val)
{
_con.push_back(val);
}
void pop()
{
_con.pop_back();
}
const T& top()
{
return _con.back();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
所以当我们实现顺序栈还是链式栈的时候,我们可以不用从头开始写了,可以使用适配器进行封装
当然,当我们想要换容器进行适配的时候
stack<int, vector<int>> st; // vector做适配器
stack<int, list<int>> st; // list做适配器
stack<int> st; // 默认deque做适配器
(四)逆波兰表达式求值
逆波兰表达式又称为后缀表达式,那什么是后缀表达式呢
- 操作符位于操作数后面
- 消除了括号
逆波兰表达式的的计算方法是从左到右遍历一遍,遇到操作数就入栈,遇到操作符就区栈顶的两个元素进行计算,然后将计算结果再次入栈,最后的计算结果就是栈顶元素的值
对于逆波兰表达式求值是比较简单的,那么问题就是如何将我们的中缀表达式转换为后缀表达式
思路:
- 将操作符入栈,操作数直接放到结果当中
- 当此刻的操作符的优先级低于栈顶元素的优先级,那么就说明可以先计算此时的结果,所以将弹出栈顶元素并且放到结果当中
- 遇到左括号直接入栈,或者此时的栈顶元素优先级小于当前符号的优先级,同样的入栈
- 遇到右括号,那么将括号之间的符号全部弹出,放到结果当中
- 处理一下小细节,当
-
为 负号,而不是运算符,需要对其处理,出现时表达式开头,出现在括号内开头
#include<string>
#include<unordered_map>
#include"Stack.h"
int main()
{
unordered_map<char, int>m;
m.insert(make_pair('+', 1));
m.insert(make_pair('-', 1));
m.insert(make_pair('*', 2));
m.insert(make_pair('/', 2));
m.insert(make_pair('(', 3));
m.insert(make_pair(')', 3));
mystack::stack<char>sympol;
queue<string>ans;
string str;
cin >> str;
int n = str.size();
int pre = 1;
int i = 0, start = 0;
while (i < n)
{
//处理前置符号
if (pre == 1 && (str[i] == '+' || str[i] == '-'))
{
if (str[i] == '+')
start++;
i++;
}
//当前是数字
while (i < n && str[i] >= '0' && str[i] <= '9')
i++;
if (i > start)
{
ans.push(str.substr(start, i - start));
pre = 0;
}
//当前是符号
if (m.find(str[i]) != m.end())
{
if (str[i] == '(')
pre = 1;
//当前没有符号
if (sympol.empty())
sympol.push(str[i]);
//当前有符号
else
{
if (str[i] == ')')
{
while (!sympol.empty() && sympol.top() != '(')
{
string tmp;
tmp.push_back(sympol.top());
sympol.pop();
ans.push(tmp);
}
sympol.pop();
}
else if (str[i] == '(' || m[str[i]] > m[sympol.top()])
sympol.push(str[i]);
else
{
//while的原因是前面是* / ,而此时是+ -
while (!sympol.empty() && sympol.top() != '(' && m[sympol.top()] >= m[str[i]])
{
string tmp;
tmp.push_back(sympol.top());
sympol.pop();
ans.push(tmp);
}
sympol.push(str[i]);
}
}
}
i++;
start = i;
}
while (!sympol.empty())
{
string tmp;
tmp.push_back(sympol.top());
sympol.pop();
ans.push(tmp);
}
return 0;
}
那当此时的中缀表达式转为后缀表达式之后,那么就很好计算了,只需要从左往右遍历一遍就可以,就可以完成计算
vector<string> popexp;
while (!ans.empty())
{
string front = ans.front();
popexp.push_back(front);
cout << front << " ";
ans.pop();
}
mystack::stack<int> num;
for (int i = 0; i < popexp.size(); i++)
{
if (popexp[i] == "+" || popexp[i] == "-" || popexp[i] == "*" || popexp[i] == "/")
{
int b = num.top(); num.pop();
int a = num.top(); num.pop();
int ret = 0;
if (popexp[i] == "+")
ret = a + b;
else if (popexp[i] == "-")
ret = a - b;
else if (popexp[i] == "*")
ret = a * b;
else
ret = a / b;
num.push(ret);
}
else
num.push(stoi(popexp[i]));
}
cout <<endl<< num.top() << endl;
(总结)
- 1、首先,我们通过文档的介绍了解了有关STL库关于stack的基本介绍,知道了在库中是一个模板类
- 2 、其次给大吉介绍了有关容器适配器的基本知识
- 3、接下来,我们简单的使用了一下stack,知道了用法
- 4、紧接着,我们手动的去实现了一个stack,相比之前的stack的实现就显得十分简单了
- 5、中缀表达式转为逆波兰表达式,然后在求值