中缀表达式转为后缀表达式求值(栈和队列的应用)

1. 题目描述:

读入一个只包含+,-,*,/,(,)的整数范围内的计算表达式,计算该表达式的值,输出的结构为double类型的一个值(对于除法的运算是有可能带有小数的)

2. 思路分析:

① 首先这道题目使用常规的方法是很难正确解决出来的,因为涉及到了运算符的优先级的问题,而且表达式中还存在着左括号与右括号的优先级问题,所以我们在读入字符串的时候是判断运算符的优先级的时候特别麻烦,所以来说常规的方法是很难正确解决出来的

② 经典的方法是将我们在控制台读入的中缀表达式的字符串转化为计算机能够很好处理没有带上括号的后缀表达式,这样就可以计算机出任何包含上面题目要求的计算表达式了

③ 知道了解决的方法之后我们还需要知道中缀表达式转为后缀表达式的一些规则和注意事项,其中需要使用到栈和队列两个数据结构来存储中间结果,下面是具体的一些规则:

1)设立一个操作符栈,用来存放操作符,设立一个数组或者是队列来存放中间执行过程中生成的后缀表达式

2)从左到右扫描中缀表达式,碰到操作数将其放进存放到后缀表达式中的队列中,但是有可能操作数的位数是大于1的,所以我们需要使用一个循环来将这些数字转为int整型然后放进队列中

遇到的是操作符op的情况:

如果碰到左括号那么直接将左括号压入到操作符栈,不是左括号的话我们需要判断当前操作符op的优先级是否大于了栈顶操作符的优先级,比如*和/的优先级是大于+和-的,*和/之间的优先级是一样的,+和-的优先级也是一样的,如果小于等于,那么就需要将操作符栈中的符号弹出来知道遇到栈顶的操作符的优先级是大于当前操作符op的,而且在将弹出来的元素加入到后缀表达式中,并且将当前的操作符压入到操作符栈中

我们可以在一开始的时候使用Java中的Map或者是C++标准模板库中的map来建立映射关系,规定操作符的优先级顺序,将加法和减法的优先级设置为1,将乘法和除法的优先级设置为2那么就可以表示操作符之间的优先级的关系了

其中还有一个细节的问题是,比如在控制台输入一个中缀表达式为:12-23-45-2*(1-2),从左到右扫描中缀表达式的时候发现左括号是直接入栈的,然后后面的时候遇到了减号假如我们在循环的条件中没有一个判断当前操作符的栈顶元素不是“(”才可以执行弹出操作的话那么就会出现空指针的异常,因为我们在map中的映射中是没有“(”的映射的,所以直接从操作符栈中取出栈顶操作符的话那么就会出现异常(栈顶元素是左括号)所以我们需要提前进行判断一下,也就是说假如其他的操作符遇到了栈顶是左括号的情况那么我们直接压入操作符栈即可(oper.peek().op !="(")

while(!oper.isEmpty() && oper.peek().op != '(' && prior.get(s.charAt(i)) <=             
  prior.get(oper.peek().op)){
				res.add(oper.pop());
}
			

3)重复上面的步骤知道扫描后缀表达式完成,假如操作符栈还存在有元素我们需要将其加入到后缀表达式中

④ 我们从上面的步骤将后缀表达式存放在了一个队列中,下面进行计算后缀表达式的值

扫描队列,如果是操作数那么直接压入到操作符栈中,因为上一步中我们的操作符栈是变为空栈了(可以用来存放计算结果)如果是操作符那么就连续弹出两个操作数(从栈中弹出)然后将新的操作数压入到栈中,反复扫描队列知道队列最后为空,那么栈中也最后只剩下一个值,这个值就是我们需要计算的表达式的值

其中遇到的问题就是那么优先级比栈顶元素的优先级小于等于的时候取出栈顶元素,但是是左括号的时候map中没有映射出现的空指针异常

3. 具体的代码如下:

Java代码:

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
public class Main {
	static Stack oper = new Stack();
	static Queue res = new LinkedList();
	static Map prior = new HashMap<>();
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
        //注意这里不能够使用next方法拉接收因为它碰到空格之后会停止接收
		String s = sc.nextLine();
		s = s.replaceAll(" ", "");
		prior.put('+', 1);
		prior.put('-', 1);
		prior.put('*', 2);
		prior.put('/', 2);
		change(s);
		System.out.println(cal());
		sc.close();
	}

	private static void change(String s) {
		Node temp;
		for(int i = 0; i < s.length();){
			if(s.charAt(i) == ')'){
				while(!oper.isEmpty() && oper.peek().op != '('){
					res.add(oper.pop());
				}
				oper.pop();
				++i;
			}else if(s.charAt(i) == '('){
				temp = new Node();
				temp.flag = false;
				temp.op = s.charAt(i);
				oper.add(temp);
				i++;
			}else if(s.charAt(i) >= '0' && s.charAt(i) <= '9'){
				temp = new Node();
				temp.flag = true;
				temp.num = s.charAt(i) - '0';
				i++;
				while(i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9'){
					temp.num = temp.num * 10 + (s.charAt(i) - '0');
					++i;
				}
				res.add(temp);
			}else{
				temp = new Node();
				temp.flag = false;
				while(!oper.isEmpty() && oper.peek().op != '(' && prior.get(s.charAt(i)) <= prior.get(oper.peek().op)){
					res.add(oper.pop());
				}
				temp.op = s.charAt(i);
				oper.push(temp);
				++i;
			}
		}
		while(!oper.isEmpty()){
			res.add(oper.pop());
		}
	}
	
	private static double cal() {
		double num1;
		double num2;
		while(!res.isEmpty()){
			Node pop = res.poll();
			if(pop.flag){
				oper.push(pop);
			}else{
				num2 = oper.pop().num;
				num1 = oper.pop().num;
				if(pop.op == '+') pop.num = num1 + num2;
				else if(pop.op == '-') pop.num = num1 - num2;
				else if(pop.op == '*') pop.num = num1 * num2;
				else{
					pop.num = num1 / num2;
				}
				oper.push(pop);
			}
		}
		return oper.peek().num;
	}
	
	public static class Node{
		double num;
		char op;
		boolean flag;
	}
}

C++代码:

#include
#include
#include
#include
#include
#include
using namespace std;
struct node{
	double num;//操作数
	char op;
	bool flag;
}; 
string str;
stack s; //操作符栈 
queue q; //后缀表达式序列 
map prior;
void change(){
	double num;
	node temp;
	for(int i = 0; i < str.length(); ){
		if(str[i] == ')'){
			while(!s.empty() && s.top().op != '('){
				q.push(s.top());
				s.pop();
			}
			//弹出左括号 
			s.pop();
			i++;
		}
		else if(str[i] == '('){
			temp.flag = false;
			temp.op = str[i];
			s.push(temp);
			i++;
		}
		else if(str[i] >= '0' && str[i] <= '9'){
			temp.flag = true;
			temp.num = str[i++] - '0';
			while(i < str.length() && str[i] >= '0' && str[i] <= '9'){
				temp.num = temp.num * 10 + (str[i] - '0');
				i++;
			}
			q.push(temp);
		}
		else{
			temp.flag = false;
			//需要增加一个条件是不不能够遇上左括号 否则在map中找不到映射为null程序就直接崩掉了 
			//增加了下面的判断是否是(程序就正确了 
			while(!s.empty() && s.top().op != '(' && prior[str[i]] <= prior[s.top().op]){
				q.push(s.top());
				s.pop();	
			}
			temp.op = str[i];
			s.push(temp);
			++i;
		}
		
	}
	while(!s.empty()){
		q.push(s.top());
		s.pop();
	}
} 

double cal(){
	double temp1, temp2;
	node cur, temp;
	while(!q.empty()){
		cur = q.front();
		q.pop();
		if(cur.flag == true) s.push(cur);
		else{
			temp2 = s.top().num;
			s.pop();
			temp1 = s.top().num;
			s.pop();
			temp.flag = true;
			if(cur.op == '+') temp.num = temp1 + temp2;
			else if(cur.op == '-') temp.num = temp1 - temp2;
			else if(cur.op == '*') temp.num = temp1 * temp2;
			else{
				temp.num = temp1 / temp2;	
			}
			s.push(temp);
		}
	}
		return s.top().num;
}

int main(void){
	prior['+'] = prior['-'] = 1;
	prior['*'] = prior['/'] = 2;
	while(getline(cin, str), str != "0"){
		for(string::iterator it = str.end(); it != str.begin(); --it){
			if(*it == ' ') {
				str.erase(it);
			}
		}
		while(!s.empty()){
			 s.pop();
		}
		change();
		cal();
		printf("%.2f\n", cal());
	}
	return 0;
}

 

你可能感兴趣的