java解析字符串表达式--逆波兰表达式的生成

基础知识:

逆波兰表达式,它的语法规定,表达式必须以逆波兰表达式的方式给出。逆波兰表达式又叫做后缀表达式。这个知识点在数据结构和编译原理这两门课程中都有介绍,下面是一些例子:

举例说明:

首先约定表达式中运算符的优先级,从大到小依次为:()、* 和 /、+ 和 -。暂时只考虑四则运算。

顺序读取字符串表达式,规则:

读到的是操作数,直接输出;
读到的是操作符(+-*/)(记为read),将其与栈顶的操作符(记为top)进行优先级比较:read>top,read入栈,继续读下一个;read≤top,top出栈,并输出到list中,read继续和新的top比较;top为空,read直接入栈;若top是“(”,read直接入栈,因为“(”优先级最高;
括号的处理:读到左括号“(”,直接将其压入栈中,并且除非遇到右括号“)”,“(”是不会弹出的;读到右括号“)”,将“(”之上的元素全部依次输出,并弹出“(”但不输出;
准备:一个栈stack暂存运算符,一个list存放输出,仍以6 * ( 5 + ( 2 + 3) * 8 + 3)为例。

1.读到“6”,直接输出【list:6;stack:】
2.读到“*”,与栈顶top比较,top为空,“*”入栈【list:6;stack:*】
3.读到“(”,直接入栈【list:6;stack:*,(】
4.读到“5”,直接输出【list:6,5;stack:*,(】
5.读到“+”,与栈顶top比较,“+”<“(”,入栈【list:6,5;stack:*,(,+】
6.读到“(”,直接入栈【list:6,5;stack:*,(,+,(】
7.读到“2”,直接输出【list:6,5,2,;stack:*,(,+,(】
8.读到“+”,与栈顶“(”比较,“+”<“(”,入栈【list:6,5,2,;stack:*,(,+,(,+】
9.读到“3”,直接输出【list:6,5,2,3,;stack:*,(,+,(,+】
10.读到“)”,输出“(”之上的元素【list:6,5,2,3,+;stack:*,(,+,】
11.读到“*”,与栈顶的“+”比较, “*”>“+”,“*”入栈【list:6,5,2,3,+;stack:*,(,+,*,】
12.读到“8”,直接输出【list:6,5,2,3,+,8,;stack:*,(,+,*,】
13.读到“+”,与栈顶的“*”比较,“+”<“*”,“*”出栈并输出【list:6,5,2,3,+,8,*;stack:*,(,+,】;
14.“+”,与栈顶的“+”比较,“+”=“+”,“+”出栈并输出【list:6,5,2,3,+,8,*,+;stack:*,(,】;
15.“+”,与栈顶的“(”比较,“+”<“(”,“+”入栈【list:6,5,2,3,+,8,*,+;stack:*,(,+】;
16.读到“3”,直接输出,【list:6,5,2,3,+,8,*,+,3;stack:*,(,+】
17.读到“)”,输出“(”之上的元素,【list:6,5,2,3,+,8,*,+,3,+;stack:*,】
18.输出栈中所有剩下的,【list:6,5,2,3,+,8,*,+,3,+,*;stack:,】
19.最终的后缀表达式就是:6 5 2 3 + 8 * + 3 + *

图示如下:
image

java代码如下: 

package qiuzhitest;

import java.util.ArrayList;
import java.util.List;

public class TransformationReversePolishNotation {

	//逆波兰表达式的生成
	//这里不考虑操作数大于等于两位,没有对除数进行非0判断,也没有考虑到负数以及小数。
	//这里规定运算符的优先级从大到小为:()、*、/、-。暂时只考虑四则运算。
	/*
	 * 规则如下:
	 * 1.读到的是操作数,直接输出;
	 * 2.读到的是操作符(四则运算),记为read,将其与栈顶中的操作符(即为top)进行优先级比较,read>top,
	 * 	  则read直接进栈,继续读取下一个字符;如果read<=top,top出栈,并输出到list集合中,read继续和
	 * 	  栈顶元素比较;如果top为空,则read直接进栈;如果top为"(",那么read直接入栈,因为"("优先级最高;
	 * 3.括号的处理:读到左括号"(",直接将其进栈,只有当遇到")","("才会弹出;读到")"时,将栈中从上往下最
	 * 	  早出现的第一个"("上的元素全部一次弹出,并输出到list集合中,并弹出"(",但不输出到list集合中。
	 * */
	public static String changeNotation(String notation){
		//定义一个字符数组,将表达式转换成一个一个字符
		char[] arr=notation.toCharArray();
		//定义一个字符串数组,用来处理字符数组中的字符
		String[] myArr=new String[arr.length];
		//处理字符数组中的字符为操作符的情况下,添加权重,表示优先级
		for(int i=0;i<arr.length;i++){
			//如果字符为0-9,则不做处理直接加入到myArr字符串数组中
			if(arr[i]>='0'&&arr[i]<='9'){
				myArr[i]=arr[i]+"";
			}else{//不是数字的情况下
				//如果是+、-的话,则给字符加一个权重1,并加入到字符串数组汇总
				if(arr[i]=='+'||arr[i]=='-'){
					myArr[i]=arr[i]+"1";
				}else if(arr[i]=='*'||arr[i]=='/'){//如果是*、/的话就加权重2
					myArr[i]=arr[i]+"2";
				}else if(arr[i]=='('){//如果是'('的话,就加权重3
					myArr[i]=arr[i]+"3";
				}else{//否则的话该字符一定是')',直接加入字符串数组,不做处理
					myArr[i]=arr[i]+"";
				}
			}
		}
		//list集合用来存放逆波兰表达式的输出结果
		List<String> list=new ArrayList<String>();
		//定义一个栈,用来处理操作符的优先级
		SequenceStack<String> stack=new SequenceStack<String>();
		//循环遍历整个表达式
		for(int i=0;i<arr.length;i++){
			//如果read是数字,则直接输出到list中
			if(arr[i]>='0'&&arr[i]<='9'){
				list.add(arr[i]+"");
			}else if(arr[i]=='('){//如果read是'('的话直接压栈(入栈的是对应位置的myArr字符串)
				stack.push(myArr[i]);
			}else if(arr[i]==')'){//如果read是')'的话,则弹出栈中第一个'('上的元素
				while(!stack.get().equals("(3")){//这里不需要进行栈空判断,因为不可能为空
					list.add(stack.pop().substring(0,1));//将'('上的元素输出到list集合中,不包括'('
				}
				//弹出'('
				stack.pop();
			}else{//如果上面的都不满足的话,说明read为四则运算符
				if(stack.isEmpty()){//如果栈为空,那么则直接将对应位置的myArr字符串入栈
					stack.push(myArr[i]);
				}else{//否则栈不为空,比较top和read的权重
					//获取read字符的权重
					int num1=Integer.parseInt(myArr[i].substring(1));
					//获取top字符的权重
					int num2=Integer.parseInt(stack.get().substring(1));
					//如果top字符的权重为3,说明是'(',这read直接进栈,或者权重read>top,也直接进栈
					if(num2==3||num1>num2){
						stack.push(myArr[i]);
					}else{//否则的话说明read<=top,则一直弹出栈顶元素,并输出到list集合中,直到将操作符压栈为止
						while(num1<=num2&&num2!=3){//如果top的权重不等于3或者read<=top,则一直出栈
							list.add(stack.pop().substring(0,1));
							//重新获取top的权重
							num2=Integer.parseInt(stack.get().substring(1));				
						}
						//将read压栈
						stack.push(myArr[i]);
					}					
				}				
			}
			System.out.println("字符:"+arr[i]);
			System.out.println("list: "+list.toString());
			System.out.println("stack: "+stack.toString());
		}
		//表达式循环判断结束后,弹出栈中的所有元素,并输出到list集合中
		while(!stack.isEmpty()){
			list.add(stack.pop().substring(0,1));
		}
		//将list集合中的数据转为逆波兰表达式
		String s="";
		for(int i=0;i<list.size();i++){
			s+=list.get(i);
		}
		return s;
	}
	
	public static void main(String[] args){
		String notation="6*(5+(2+3)*8+3)";
		System.out.println("中缀表达式为: "+notation);
		System.out.println("----------转换过程-----------");
		String s=changeNotation(notation);
		System.out.println("----------转换过程-----------");
		System.out.println("逆波兰表达式为: "+s);
                //如何计算逆波兰表达式我们在下一篇博客分析
		System.out.println("计算结果为: "+CountReversePolishNotation.countNotation(s));
	}
}

输出结果: 

中缀表达式为: 6*(5+(2+3)*8+3)
----------转换过程-----------
字符:6
list: [6]
stack: []
字符:*
list: [6]
stack: [*2]
字符:(
list: [6]
stack: [*2,(3]
字符:5
list: [6, 5]
stack: [*2,(3]
字符:+
list: [6, 5]
stack: [*2,(3,+1]
字符:(
list: [6, 5]
stack: [*2,(3,+1,(3]
字符:2
list: [6, 5, 2]
stack: [*2,(3,+1,(3]
字符:+
list: [6, 5, 2]
stack: [*2,(3,+1,(3,+1]
字符:3
list: [6, 5, 2, 3]
stack: [*2,(3,+1,(3,+1]
字符:)
list: [6, 5, 2, 3, +]
stack: [*2,(3,+1]
字符:*
list: [6, 5, 2, 3, +]
stack: [*2,(3,+1,*2]
字符:8
list: [6, 5, 2, 3, +, 8]
stack: [*2,(3,+1,*2]
字符:+
list: [6, 5, 2, 3, +, 8, *, +]
stack: [*2,(3,+1]
字符:3
list: [6, 5, 2, 3, +, 8, *, +, 3]
stack: [*2,(3,+1]
字符:)
list: [6, 5, 2, 3, +, 8, *, +, 3, +]
stack: [*2]
----------转换过程-----------
逆波兰表达式为: 6523+8*+3+*
计算结果为: 288

分析拓展:

至此,中缀表达式到后缀表达式的任务已经全部完成!

但是需要注意的是,本文中只是做了当运算数据为个位数的处理,当为多位数时该怎么做呢?

另外当遇到“/”处时,要对被除数进行非0判断;

另外还可以考虑含有负数的运算,含有小数的以及幂运算

那都是数据如何处理的问题了,整体思路应该都差不多!

下一回合我们将考虑如何用java实现逆波兰表达式的计算。

计算逆波兰表达式:https://blog.csdn.net/qq_41300571/article/details/83376961

参考文章:https://blog.csdn.net/bruce_6/article/details/39205837

猜你喜欢

转载自blog.csdn.net/qq_41300571/article/details/83376757
今日推荐