各种表达式
前缀(波兰),中缀,后缀表达式(逆波兰)
中缀:(3+4)*5-6
前缀: - * + 3 4 5 6(从右至左进行运算)
后缀:3 4 + 5 * 6 - (从左向右进行运算)
前面,我们利用栈实现了一个简单的中缀表达式计算器。实际在计算机中,更多利用的是由后缀表达式来计算。下面。我们讨论如何使用后缀表达式计算结果 和 如何将 中缀表达式 转化为后缀表达式。
逆波兰表达式计算
对这样一个逆波兰表达式3 4 + 5 * 6 - ,中缀【(3+4)*5-6)】我们采用以下算法步骤来计算最终结果
- 建立一个数栈stack1
- 建立一个索引来遍历接受的表达式,初始化index=0
- 遍历表达式,若遇到数字直接入栈,遇到符号则从stack1中弹栈两个数字,并将运算结果重新压栈到stack1中。
- 重复步骤3直到表达式遍历完毕。
因此对于3 4 + 5 * 6 - 我们有以下步骤:
遍历表达式item | 操作 | stack1中元素(从栈底到栈顶) |
---|---|---|
0 | 3入栈 | 3 |
4 | 4入栈 | 3,4 |
+ | 4,3弹栈并运算结果为7,7入栈 | 7 |
5 | 5入栈 | 7,5 |
* | 5,7弹栈运算结果的35,35入栈 | 35 |
6 | 6入栈 | 35,6 |
- | 6,35弹栈运算35-6=29,29入栈 | 29 |
运算结束,栈顶元素即为最终运算结果 | 29 |
Note that 注意每次弹栈两个进行运算时,后弹出的元素应该放在运算符前面。比如对于除法操作,应该是 后弹出的元素/ 先弹出的元素 作为运算结果。
这里,我们采用java自带的java.util.Stack进行程序编写
package com.like.java.data_structure.stack;
import java.util.Arrays;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
// 一个逆波兰后缀表达式
String expString = "300 4 + 5 * 6 -";
// 建立数栈
String[] list = expString.split(" ");
System.out.println(Arrays.toString(list));
Stack<Integer> numStack = new Stack<Integer>();
for(String each:list)
{
if(each.matches("\\d+"))
{
numStack.push(Integer.parseInt(each));
continue;
}
int num2 = numStack.pop();
int num1 = numStack.pop();
int res = 0;
if(each.contentEquals("+"))
{
res = num2 + num1;
}else if(each.contentEquals("-"))
{
res = num1 - num2;
}else if(each.contentEquals("*"))
{
res = num1 * num2;
}else {
res = num1/num2;
}
numStack.push(res);
}
System.out.println("运算结果:"+numStack.pop());
}
}
中缀表达式->后缀表达式
例如对于这样一个中缀表达式:1 + ( ( 2 + 3 ) * 4 ) - 5,转为后缀表达式思想
1)创建两个栈:运算符栈s1和存储中间结果得栈s2(也可以采用普通的数组结构)
2)从左到右依次扫描中缀表达式
3)遇到操作数时,将其压入s2
4)遇到运算符时,比较与s1栈顶元素运算符得优先级
4.1 若s1为空或者栈顶元素为(,直接压栈
4.2 若当前运算符大于栈顶运算符,直接压栈‘
4.3 否则,s1栈顶弹栈到s2,并拿当前运算符和下一个栈顶元素继续比较,转4.1
5)遇到括号时(括号不算运算符)
5.1 若是左括号,直接压栈
5.2 若是右括号,s1弹栈并压入s2直到遇到一个左括号,舍弃左右括号
6)重复2-5直到表达式最右边
7) 将s1剩余元素依次弹栈并压入s2
8)依次弹出s2元素,逆序即为最终得后缀表达式
Note that: s2只有压栈操作,后面还需要逆序输出,其实可以用一个list来替代
Java代码
package com.like.java.data_structure.stack;
import java.util.ArrayList;
import java.util.Stack;
import java.util.concurrent.PriorityBlockingQueue;
// 中缀表达式转为后缀表达式
public class parseSuffix {
public static void main(String[] args) {
String expression = "1 + ( ( 2 + 3 ) * 4 ) - 5";
String[] val = expression.split(" ");
int index = 0;
// 利用一个栈和数组,数组用于存储中间运算结果
Stack<String> stack = new Stack<String>();
ArrayList<String> list = new ArrayList<String>();
String c = " ";
while(index < val.length)
{
// 当前元素
c = val[index];
// 如果是数字直接加到list中
if(Character.isDigit(c.charAt(0)))
{
list.add(c);
}
// 否则,如果栈为空,或者栈顶元素为(, 或者要处理的元素为(,直接入栈
else if(stack.empty() || stack.peek().equals("(") || c.equals("("))
{
stack.push(c);
}
// 若是运算符,则比较优先级。若优先级不高于栈顶,则栈顶元素不断出栈到list中。
else if( c.contentEquals("*")|| c.contentEquals("/")|| c.contentEquals("+")||c.contentEquals("-"))
{
System.out.println("符号遇到");
while(!stack.empty())
{
char cur = c.charAt(0);
char top = stack.peek().charAt(0);
System.out.println("比较符号");
System.out.println(cur+":"+top);
if(Priority(cur)>Priority(top))
{
stack.push(c);
break;
}else {
list.add(stack.pop());
}
}
}
// 若遇到右括号,则不断弹栈直到遇到一个左括号
if(c.contentEquals(")"))
{
while(!stack.empty() && !stack.peek().contentEquals("("))
{
list.add(stack.pop());
}
// 弹出左括号
stack.pop();
}
index ++;
}
System.out.println(list);
}
// 设定运算符优先级
public static int Priority(char oper)
{
switch(oper)
{
case '+':
case '-':
return 1;
case '*':
return 2;
case '/':
return 2;
default:
return 0;
}
}
}