1.栈
栈是一种后进先出的数据结构。向栈中插入新元素称为进栈、入栈、压栈,使新元素成为栈顶元素;从栈中删除一个元素称为出栈、退栈,于其相邻的元素成为栈顶元素。栈顶指针是指向栈顶元素的标记,通常记为TOP,在数组栈中TOP是一个int型变量,而在链表栈中是一个int*类型指针。数组栈中数组下标从0开始,TOP为栈顶元素的下标。
2.栈的基本操作
以数组栈为例
(1)获取栈内元素个数
int size(){
return TOP+1;
}
(2)是否为空
TOP=-1表示栈内没有元素
bool empty(){
if(TOP==-1)
return true;
return false;
}
(3)入栈
void push(int x){
TOP++;
s[TOP]=x;
}
(4)出栈
void pop(){
POP--;
}
(5)获得栈顶元素
int top(){
return s[TOP];
}
注意:出栈和获取栈顶元素两项操作前要先判断栈是否为空。
(6)清空
void clear(){
TOP=-1;
}
3.stl中的栈
(1)头文件
#include <stack>
using namespace std;
(2)定义
typename可以是任意基本数据类型和容器
stack<typename> s;
(3)常用函数
1.push()
push(x)将元素x压栈
2.pop()
弹出栈顶元素
3.top()
获得栈顶元素
4.empty()
判断栈是否为空,空返回true,否则返回false
5.size()
返回栈内元素个数
一个简单实例:
#include <stdio.h>
#include <stack>
using namespace std;
int main(){
stack<int> s;
for(int i=1;i<=5;i++)
s.push(i);
printf("%d\n",s.top()); //5
while(!s.empty()){
s.pop();
}
printf("%d\n",s.size()); //0
return 0;
}
4.经典问题之括号匹配
大致题意就是给出一个只含有(、)、{、}、[、]的字符串,判断是否满足如下规则:
1.左括号和对应右括号匹配
2.左括号按照正确的顺序匹配
示例1:
输入:([{}])
输出:Yes
示例2:
输入:()[}
输出:No
思路:
1.遍历整个字符串。
2.如果遇到左括号([{,将括号入栈。
3.如果遇到右括号)]},若此时栈为空,直接匹配失败。不为空的话将栈顶元素与右括号进行匹配,不匹配的话匹配失败,匹配的话则将栈顶元素出栈并继续循环。
4.字符串遍历结束如果中途没有出现匹配失败并且最终栈为空,则整个字符串匹配成功。
代码:
#include <stdio.h>
#include <iostream>
#include <string>
#include <stack>
using namespace std;
bool match(char a,char b){
if(a=='(' && b==')')
return true;
else if(a=='[' && b==']')
return true;
else if(a=='{' && b=='}')
return true;
else
return false;
}
int main(){
string str;
cin>>str;
stack<char> s;
int flag=0;
for(string::iterator it=str.begin();it!=str.end();it++){
if(*it=='(' || *it=='[' || *it=='{')
s.push(*it);
else{
if(s.empty()){
printf("No\n");
flag=-1;
break;
}
char t=s.top();
if(match(t,*it)==false){
printf("No\n");
flag=-1;
break;
}
s.pop();
flag=1;
}
}
if(flag=1 && s.empty())
printf("Yes\n");
return 0;
}
5.经典问题之后缀表达式(逆波兰表达式)
(1)中缀表达式转后缀表达式
中缀表达式就是平常最常见的算术表达式,运算符在运算数中间,需要考虑运算符的优先级。
后缀表达式中运算符在运算数之后,无需考虑优先级,是计算机方便运算的一种算术表达式。
示例:
输入:(2*(9+6/3-5)+4)
输出:2 9 6 3 / + 5 - * 4 +
思路:
1.建立一个栈存放操作符
2.从左到右遍历字符串,遇到左括号进栈
3.遇到操作符时,如果优先级大于栈顶元素的优先级则入栈,否则不断弹出栈顶元素加入后缀表达式,直到满足该操作符优先级大于栈顶元素优先级 (优先级顺序:乘除>加减>括号)
4.遇到操作数直接加入后缀表达式中
5.遇到右括号,不断弹出栈顶元素加入后缀表达式直到遇到左括号
6.若字符串遍历完栈非空,则将栈顶元素依次弹出加入后缀表达式中
代码:
我这里直接使用了字符串存放后缀表达式(这种方法局限性较大)
#include <stdio.h>
#include <iostream>
#include <stack>
#include <ctype.h>
using namespace std;
bool compare(char a,char b){
if((a=='*' || a=='/') && (b=='+' || b=='-' || b=='(' || b==')'))
return true;
else if((a=='+' || a=='-') && (b=='(' || b==')'))
return true;
else
return false;
}
int main(){
stack<char> op;
string s;
string ans;
cin>>s;
for(string::iterator it=s.begin();it!=s.end();it++){
if(isdigit(*it)){
ans+=*it;
ans+=' ';
}
else if(*it=='('){
op.push(*it);
}
else if(*it==')'){
while(op.top()!='('){
ans+=op.top();
ans+=' ';
op.pop();
}
op.pop();
}
else{
if(op.empty()){
op.push(*it);
continue;
}
char t=op.top();
if(compare(*it,t)==true){
op.push(*it);
}
else{
while(compare(*it,op.top())==false){
ans+=op.top();
ans+=' ';
op.pop();
if(op.empty())
break;
}
op.push(*it);
}
}
}
while(!op.empty()){
ans+=op.top();
ans+=' ';
op.pop();
}
cout<<ans<<endl;
return 0;
}
(2)后缀表达式求值
得到后缀表达式之后将值求出来
示例:
输入:2 9 6 3 / + 5 - * 4 +
输出:16.00
思路:
1.从左到右遍历后缀表达式,遇到操作数进栈
2.遇到操作符,从栈中弹出两个操作数(后弹出的是第一操作数),将两个操作数运算的结果压栈
3.整个后缀表达式遍历结束,栈中唯一一个元素就是所求的值
采用结构体、队列、映射等对代码进行改良后的整体代码:
#include <stdio.h>
#include <iostream>
#include <stack>
#include <queue>
#include <map>
#include <string>
#include <ctype.h>
using namespace std;
struct node{
bool flag; //flag=true表示操作数,=false表示操作符
float num;
char op;
};
string str;
stack<node> s;
queue<node> q;
map<char,int> m;
//中缀转后缀
void expression(){
for(string::iterator it=str.begin();it!=str.end();it++){
node t;
if(isdigit(*it)){
t.flag=true;
t.num=*it-'0';
string::iterator it1=it+1;
while(isdigit(*it1) && it1!=str.end()){
t.num=t.num*10+*it1-'0';
it1++;
}
q.push(t);
it=it1-1;
}
else if(*it=='('){
t.flag=false;
t.op=*it;
s.push(t);
}
else if(*it==')'){
t.flag=false;
t.op=*it;
while(s.top().op!='('){
q.push(s.top());
s.pop();
}
s.pop();
}
else{
t.flag=false;
t.op=*it;
if(s.empty()){
s.push(t);
continue;
}
if(m[*it]>m[s.top().op])
s.push(t);
else{
while(m[*it]<=m[s.top().op]){
q.push(s.top());
s.pop();
if(s.empty())
break;
}
s.push(t);
}
}
}
while(!s.empty()){
q.push(s.top());
s.pop();
}
}
//后缀表达式求值
float cal(){
while(!q.empty()){
node t=q.front();
q.pop();
if(t.flag==true){
s.push(t);
}
else{
node c;
c.flag=true;
float a=s.top().num;
s.pop();
float b=s.top().num;
s.pop();
if(t.op=='+')
c.num=b+a;
else if(t.op=='-')
c.num=b-a;
else if(t.op=='*')
c.num=b*a;
else if(t.op=='/')
c.num=b/a;
s.push(c);
}
}
return s.top().num;
}
int main(){
m['+']=1;
m['-']=1;
m['*']=2;
m['/']=2;
getline(cin,str);
for(string::iterator it=str.begin();it!=str.end();it++){
if(*it==' ')
str.erase(it);
}
expression();
/* 检查后缀表达式是否正确
while(!q.empty()){
if(q.front().flag==true)
printf("%f",q.front().num);
else
printf("%c",q.front().op);
q.pop();
}*/
printf("%.2f\n",cal());
return 0;
}
几个注意点:
1.输入字符串可能有空格和左右括号,需分别处理
2.操作数可能是两位以上的,因此改用结构体
3.处理过程中几个条件判断要考虑栈空的情况,不然可能会卡死在循环中