蓝桥杯 ALGO-156 表达式计算 逆波兰和递归的两种题解

题目内容

题目内容

在解决这道问题的时候,我参考了X_1996的讲解。他的讲解非常棒,仔细且详细,但存在一些问题,所以我决定写下这篇随笔。

逆波兰方法

算法思路

算法思路

解题分析

中缀转后缀

  1. 从左至右扫描中缀表达式
  2. 若读取的是操作数,则将该操作数存入操作数堆栈
  3. 若读取的是运算符:
    • 该运算符为左括号"(",则直接存入运算符堆栈。
    • 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止
    • 该运算符为非括号运算符:
      • 若运算符堆栈为空,直接存入
      • 若运算符堆栈栈顶的运算符为左括号,则直接存入运算符堆栈
      • 若比运算符堆栈栈顶的运算符优先级高,则直接存入运算符堆栈
      • 若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。
  4. 当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

在使用用C++实现这部分功能的时候,因为数据类型不同,实际上不能将运算符压入操作数堆栈;且操作数堆栈并没有用到堆栈的特性,所以我使用了string来实现“操作数堆栈”,使用','来作为数字的结束符。

使用map来进行运算符优先级判断。在使用时,yx['+']即为'+'的优先级。

map<char,int> yx;
yx['+']=2;yx['-']=2;yx['*']=3;yx['/']=3;yx['(']=1;yx[')']=0;

opera为运算符栈,instr是输入的中缀表达式,nums为“操作数堆栈”。

stack<char> opera;
string instr,nums;
cin>>instr;
for(int i=0;i<instr.length();i++){
	if(isdigit(instr[i])){                //遇到操作数直接追加到nums
		nums.append(1,instr[i]);
		if(!isdigit(instr[i+1]))    //判断数字是否结束,若结束则追加','的结束符
			nums.append(1,',');
	}
         //若运算符堆栈为空或运算符堆栈栈顶的运算符优先级高或运算符为左括号,则压入运算符堆栈
	else if(opera.empty()||yx[instr[i]]>yx[opera.top()]||instr[i]=='(')
		opera.push(instr[i]);
	else if(instr[i]==')'){                                //若该运算符为右括号
		while(opera.top()!='('){                    //则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号
			nums.append(1,opera.top());
			opera.pop();
		}
		opera.pop();
	}
        //若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。
	else{
		while(!opera.empty()&&yx[opera.top()]>=yx[instr[i]]){
			nums.append(1,opera.top());
			opera.pop();
		}
		opera.push(instr[i]);
	}
}
//扫描结束后将运算符堆栈中剩余的运算符弹出并压入操作数堆栈
while(!opera.empty()){
	nums.append(1,opera.top());
	opera.pop();
}
cout<<"nums:"<<nums<<endl;

输入中缀表达式"1+((23+34)5)-6"即可得到后缀表达式1,23,34,+5,+6, -

到这里就完成了中缀到后缀的转换

由后缀表达式计算结果

  1. 扫描后缀表达式
  2. 如果扫描到的是操作数,则将其压入操作数堆栈,并扫描下一个项目
  3. 如果扫描到的运算符,则对栈的顶上两个操作数执行该运算
  4. 将运算结果重新压入堆栈
  5. 重复步骤2-4,堆栈中即为结果值

int型的stack,作为新的操作数堆栈

stack<int> result;
for(int i=0;i<nums.length();i++){
	if(isdigit(nums[i])){        //如果扫描到操作数,则将其转换为int型,并压入操作数堆栈
		int temp=nums[i]-'0';
		for(int j=i+1;j<nums.length();j++,i++){
			if(isdigit(nums[j])){
				temp*=10;
				temp+=nums[j]-'0';
			}
			else break;
		}
		result.push(temp);
	}
	else if(nums[i]==',')continue;    //遇到分隔符','则忽略
	else {                                        //遇到运算符,取出操作数堆栈中的两个数
		int ra=result.top();
		result.pop();
		int rb=result.top();
		result.pop();
		if(nums[i]=='+')                    //进行相应的运算后重新压入操作数堆栈
			result.push(rb+ra);
		else if(nums[i]=='-')
			result.push(rb-ra);
		else if(nums[i]=='*')
			result.push(rb*ra);
		else if(nums[i]=='/')
			result.push(rb/ra);
	}	
}
cout<<result.top()<<endl;                    //最后操作数堆栈栈顶元素则为运算结果

完整代码

#include<iostream>
#include<stack> 
#include<string>
#include<map>
 
using namespace std;

int main(){
	map<char,int> yx;
	yx['+']=2;yx['-']=2;yx['*']=3;yx['/']=3;yx['(']=1;yx[')']=0;
	stack<char> opera;
	stack<int> result;
	string instr,nums;
	cin>>instr;
	for(int i=0;i<instr.length();i++){
		if(isdigit(instr[i])){
			nums.append(1,instr[i]);
			if(!isdigit(instr[i+1]))
				nums.append(1,',');
		}
		else if(opera.empty()||yx[instr[i]]>yx[opera.top()]||instr[i]=='(')
			opera.push(instr[i]);
		else if(instr[i]==')'){
			while(opera.top()!='('){
				nums.append(1,opera.top());
				opera.pop();
			}
			opera.pop();
		}
		else{
			while(!opera.empty()&&yx[opera.top()]>=yx[instr[i]]){
				nums.append(1,opera.top());
				opera.pop();
			}
			opera.push(instr[i]);
		}
	}
	while(!opera.empty()){
		nums.append(1,opera.top());
		opera.pop();
	}
	//cout<<"nums:"<<nums<<endl;
	for(int i=0;i<nums.length();i++){
		if(isdigit(nums[i])){
			int temp=nums[i]-'0';
			for(int j=i+1;j<nums.length();j++,i++){
				if(isdigit(nums[j])){
					temp*=10;
					temp+=nums[j]-'0';
				}
				else break;
			}
			result.push(temp);
		}
		else if(nums[i]==',')continue;
		else {
			int ra=result.top();
			result.pop();
			int rb=result.top();
			result.pop();
			if(nums[i]=='+')
				result.push(ra+rb);
			else if(nums[i]=='-')
				result.push(rb-ra);
			else if(nums[i]=='*')
				result.push(ra*rb);
			else if(nums[i]=='/')
				result.push(rb/ra);
		}	
	}
	cout<<result.top()<<endl;
}

程序在蓝桥练习系统中AC

AC截图

递归法

#include<iostream>
using namespace std;

int bds();

int yz(){
	int result=0;
	if(cin.peek()=='('){
		cin.get();
		result+=bds();
		cin.get();
	}
	else cin>>result;
	return result;
}

int x(){
	int result=yz();
	while(true){
		char ch=cin.peek();
		if(ch=='*'||ch=='/'){
			cin.get();
			if(ch=='*')
				result*=x();
			else result/=x();
		}
		else break;
	}
	return result;
}

int bds(){
	int result=x();
	while(true){
		char ch=cin.peek();
		if(ch=='+'||ch=='-'){
			cin.get();
			if(ch=='+')
				result+=x();
			else result-=x();
		}
		else break;
	}
	return result;
}

int main(){
	cout<<bds()<<endl;
}

posted on 2018-02-06 20:50  jsdtxm  阅读(177)  评论(0)    收藏  举报

导航