假如现在设计一个计算器,接收用户输入,计算相应结果并输出。当用户输入为“9 + (3 - 1) * 3 + 10 / 2”时,你怎么设计使其能够输出正确的结果“20”?
后缀表达式
上述表达式中,由于我们是一个一个数字和符号输入给程序的,故如何让程序知道运算优先级是个问题(比如,程序怎么知道要先算括号里面的内容)。
我们可以通过将上面正常的表达式(中缀表达式)转换为后缀表达式,然后经过相应的操作即可获得最终结果“20”。具体的后缀表达式为:“9 3 1 - 3 * + 10 2 / +”。由于其所有的符号都在要运算的数字的后面,故为后缀表达式。
转换规则:从左到右遍历表达式的每个数字和符号,若是数字就输出,成为后缀表达式的一部分;若为符号,则判断其与栈顶符号的优先级,是“)”或优先级不高于栈顶符号,则栈顶元素依次出栈并输出(括号不必写入输出),并将当前符号进栈,一直到最终输出后缀表达式为止。最后将栈中所有元素依次出栈。
举例: 假设输入表达式为“9 + ( 3 - 1) * 3 + 10 / 2”。
- 遍历表达式,得到第一个元素为“9”,根据规则(数字直接输出),则输出“9”。输出为:9
- 第二个元素为“+”,此时栈为空,故将“+”进栈。输出为:9
- 接着遍历,第三个元素是“(”,则进栈。输出为:9
- 接下来的是数字“3”,直接输出。输出为:9 3
- 然后是“-”,进栈。输出为:9 3
6.数字“1”,直接输出。输出为:9 3 1
7.接下来是“)”,注意,根据规则,栈顶符号依次出栈,直到“(”出栈为止。输出为:9 3 1 -
8.接下来是“*”,由于乘法的优先级高于加法,故进栈。输出为:9 3 1 -
9.数字“3”,直接输出。输出为:9 3 1 - 3
10.接下来是“+”,由于加法的优先级低于乘法,则“*”出栈。出栈后,栈顶元素此时为最先进去的“+”,外面的“+”和里面的“+”优先级相等,根据规则(外面符号的优先级不高于栈顶符号,故满足要求),则里面的“+”出栈。此时栈为空,则外面的“+”进栈。输出为:9 3 1 - 3 * +
11.接着输出数字“10”。输出为:9 3 1 - 3 * + 10
12.接着为“/”,由于除法的优先级高于加法,故进栈。输出为:9 3 1 - 3 * + 10
13.最后一个为数字“2”,直接输出。输出为:9 3 1 - 3 * + 10 2
14.最后所有元素依次出栈,则输出为:9 3 1 - 3 * + 10 2 / +,即为后缀表达式。
Java代码:输入为9+(3-1)*3+10/ 2,输出为9 3 1 - 3 * + 10 2 / +
public class Transfer {
/*中缀表达式转后缀表达式*/
public static void main(String[] args) {
Stack t = new Stack(); //初始化一栈
Scanner s = new Scanner(System.in);
System.out.println("请输入表达式:");
String str = s.next();
char[] c = str.toCharArray(); //将输入的字符串表达式转成字符数组
for(int i = 0;i < c.length;i++){
if(c[i] >= '0' && c[i] <= '9'){ //判断是否为数字字符
String c1 = String.valueOf(c[i]); //将c[i]转成字符串类型
int result = Integer.parseInt(c1); //再将字符串类型的数字转成整型
while(i < c.length - 1 && c[i+1] >= '0' && c[i+1] <= '9') {
/*判断数字是否为两位或多位数字,若是则按照上面方法将其转为整型,
并依次乘10转成真正的数字,并直接输出。例如:23。c[i]=2,c[i+1]=3,
将其都转为数字后,2*10+3=23。*/
String c2 = String.valueOf(c[i + 1]);
int c3 = Integer.parseInt(c2);
result = result * 10 + c3;
i = i + 1;
}
System.out.println(result);
}else if(c[i] == ')'){ //若为),则出栈,出到(出来为止
char a = (char) t.pop();
while (a != '('){ //输入为右括号,出栈,直到左括号出现
System.out.println(a);
a = (char)t.pop();
}
}else if(c[i] == '+' || c[i] == '-'){ //输入为+-
if(t.empty()){ // 栈若为空,则直接进栈
t.push(c[i]);
}else {
do{
char p = (char)t.pop(); //先取出栈顶元素
if(p == '('){
t.push(p); //如果栈顶元素是(,则再把它放进去
}else{
System.out.println(p); //+-的优先级低于*/,若栈顶为*/,则出栈
}
}while (!t.empty() && '(' != (char)t.peek()); //栈非空且栈顶元素不是(
t.push(c[i]);
}
}else if('*' == c[i] || '/' == c[i] || '(' == c[i]){ //*/(优先级高于任何,进栈
t.push(c[i]);
}else{
System.out.println("输入错误");
}
}
while (!t.empty()){
System.out.println(t.pop()); //最后将所有元素弹出
}
}
}
表达式计算
规则: 从左到右遍历后缀表达式,若是数字则进栈,若是符号则将栈顶的两个数字出栈,进行计算后将计算结果入栈。最后栈中存的数字即为结果。
举例: 输入后缀表达式为:9 3 1 - 3 * + 10 2 / +
-
前面三个均为数字,故都进栈。
-
接下来为“-”,故将栈顶两个元素(1,3)出栈,3-1=2,入栈。
-
接着是数字“3”,进栈。
-
后面是“*”,则3和2出栈计算,结果6进栈。
-
接着是“+”,则6和9出栈计算,结果为15,进栈。
-
接着是数字10和2,进栈。
-
接下来是“/”,则2和10出栈计算,得到5,进栈。
-
最后是“+”,5和15出栈,得到20,即为开头所求结果。
Java代码:输入为9,3,1,-,3,*,+,10,2,/,+ 输出为20
public class Computer {
/*输入一个后缀表达式,计算(**数与数,数与符号,符号与符号之间用,隔开**)*/
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
Stack t = new Stack();
System.out.println("请输入后缀表达式:");
String str = s.next();
char[] c = str.toCharArray();
for (int i = 0; i < c.length; i++) {
if(c[i] == ','){
continue;
}else if(c[i] >= '0' && c[i] <= '9'){
/*同Transfer的方法,判断一个数字为几位,并入栈*/
String c1 = String.valueOf(c[i]);
int result = Integer.parseInt(c1);
while(i < c.length - 1 && c[i+1] >= '0' && c[i+1] <= '9') {
String c2 = String.valueOf(c[i + 1]);
int c3 = Integer.parseInt(c2);
result = result * 10 + c3;
i = i + 1;
}
t.push(result);
/*加减乘除均将栈顶的两个元素出栈并进行相应计算*/
}else if('+' == c[i] ){
int a1 = (int)t.pop();
int a2 = (int)t.pop();
int a3 = a2 + a1;
t.push(a3);
}else if('-' == c[i] ){
int a1 = (int)t.pop();
int a2 = (int)t.pop();
int a3 = a2 - a1;
t.push(a3);
}else if('*' == c[i] ){
int a1 = (int)t.pop();
int a2 = (int)t.pop();
int a3 = a2 * a1;
t.push(a3);
}else if('/' == c[i] ){
int a1 = (int)t.pop();
int a2 = (int)t.pop();
int a3 = a2 / a1;
t.push(a3);
}else{
System.out.println("输入错误");
}
}
System.out.println(t.pop());
}
}