目录
2.1 前缀表达式(波兰表达式 Polish Notation)
2.3 后缀表达式(逆波兰表达式 Reverse Polish Notation)
一、栈
1.1 基本介绍
栈(stack)是一种先入后出的有序列表。
栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端称为栈顶,另一端称为栈底。最先放入栈中的元素在栈底,最后放入栈中的元素在栈顶。
1.2 数组模拟栈
数组模拟栈示意图:
- maxTop表示栈大小。
- top指向栈顶的位置,初始值为-1,top==-1时栈为空,top== maxTop – 1时栈满。
- 入栈时,先将top++再添加数据。
- 出栈时,先取出数据再将top--。
代码:
public class ArrayStack {
public static void main(String[] args) {
Stack stack = new Stack(5);
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("遍历栈:");
stack.showStack();
System.out.println("出栈:");
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println("遍历栈:");
stack.showStack();
}
}
class Stack {
private int maxSize; //数组最大容量
private int top; //指向栈顶的位置
private int[] arr; //模拟栈
//初始化队列
public Stack(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
top = -1;
}
//判断栈是否已满
public boolean isFull() {
return top == maxSize - 1;
}
//判断栈是否为空
public boolean isEmpty() {
return top == -1;
}
//入栈
public void push(int n) {
if (isFull()) {
System.out.println("栈已满");
return;
}
//将top后移
top++;
//添加数据
arr[top] = n;
}
//出栈
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
//保存top对应数据
int value = arr[top];
//将top前移
top--;
//返回保存的数据
return value;
}
//遍历栈,从栈顶开始遍历
public void showStack() {
if (isEmpty()) {
System.out.println("栈为空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
}
结果:
1.3 链表模拟栈
代码:
public class LinkedListStack {
public static void main(String[] args) {
MyNode myNode1 = new MyNode(10);
MyNode myNode2 = new MyNode(20);
MyNode myNode3 = new MyNode(30);
Stack stack = new Stack();
stack.push(myNode1);
stack.push(myNode2);
stack.push(myNode3);
System.out.println("遍历栈:");
stack.showStack();
System.out.println("出栈:");
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println("遍历栈:");
stack.showStack();
}
}
class Stack {
//初始化头节点
private MyNode head = new MyNode(0);
//判断栈是否为空
public boolean isEmpty() {
return head.next == null;
}
//入栈
public void push(MyNode myNode) {
myNode.next = head.next;
head.next = myNode;
}
//出栈
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空");
}
int value = head.next.no;
head.next = head.next.next;
//返回保存的数据
return value;
}
//遍历栈,从栈顶开始遍历
public void showStack() {
if (isEmpty()) {
System.out.println("栈为空");
return;
}
//cur表示当前节点
MyNode cur = head.next;
while (cur != null) {
System.out.println(cur);
cur = cur.next;
}
}
}
class MyNode {
public int no;
public MyNode next; //指向下一个节点
//构造器
public MyNode(int no) {
this.no = no;
}
@Override
public String toString() {
return "MyNode{" +
"no=" + no +
'}';
}
}
结果:
二、栈的三种表达式
2.1 前缀表达式(波兰表达式 Polish Notation)
运算符位于操作数之前,表达式(1 + 2) * 3 - 4对应的前缀表达式为:- * + 1 2 3 4
求值步骤:从右至左扫描,遇到数字时,将数字压入栈中,遇到运算符时,弹出栈顶的两个数,进行计算并将结果入栈,重复此过程直到表达式扫描完毕,得出运算结果。
2.2 中缀表达式
常见的运算表达式,对于计算机来说不好操作,计算结果时往往会将中缀表达式转换成其他表达式(一般转换成后缀表达式)。
2.3 后缀表达式(逆波兰表达式 Reverse Polish Notation)
运算符位于操作数之后,表达式(1 + 2) * 3 - 4对应的后缀表达式为:1 2 + 3 * 4 -
求值步骤:从左至右扫描,遇到数字时,将数字压入栈中,遇到运算符时,弹出栈顶的两个数,进行计算并将结果入栈,重复此过程直到表达式扫描完毕,得出运算结果。
2.4 逆波兰表达式求值
代码:
public class ReversePolishNotation {
public static void main(String[] args) {
//定义逆波兰表达式
String rpn = "1 2 + 3 * 4 -"; //相当于(1 + 2) * 3 - 4
List<String> list = getSuffixList(rpn);
int res = calculator(list);
System.out.println(res);
}
//将逆波兰表达式转换成List集合
public static List<String> getSuffixList(String rpn) {
String[] strs = rpn.split(" ");
List<String> list = new ArrayList<>();
//遍历逆波兰表达式数组
for (String str : strs) {
list.add(str);
}
return list;
}
//计算器方法
public static int calculator(List<String> list) {
//创建数字栈
Stack<String> numStack = new Stack<>();
//遍历逆波兰表达式集合
for (String str : list) {
//匹配数字
if (!("+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str))) {
numStack.push(str);
}
//匹配运算符
else {
//数字出栈
int num2 = Integer.parseInt(numStack.pop());
int num1 = Integer.parseInt(numStack.pop());
//计算结果入栈
if (str.equals("+")) {
numStack.push(String.valueOf(num1 + num2));
} else if (str.equals("-")) {
numStack.push(String.valueOf(num1 - num2));
} else if (str.equals("*")) {
numStack.push(String.valueOf(num1 * num2));
} else if (str.equals("/")) {
numStack.push(String.valueOf(num1 / num2));
} else {
throw new RuntimeException("运算符有误!");
}
}
}
return Integer.parseInt(numStack.pop());
}
}
结果:
2.5 中缀表达式转后缀表达式
代码:
public class ParseInfixToSuffix {
public static void main(String[] args) {
//定义中缀表达式
String s = "1+((2+3-4)*5)-6";
//将中缀表达式转换成List集合
List<String> infixList = getInfixList(s);
System.out.println("中缀表达式:");
System.out.println(infixList);
//将中缀表达式List集合转换成后缀表达式List集合
List<String> suffixList = parseInfixToSuffix(infixList);
System.out.println("后缀表达式:");
System.out.println(suffixList);
}
//将中缀表达式转换成List集合
public static List<String> getInfixList(String s) {
List<String> infixList = new ArrayList<>();
//定义字符串当前坐标
int cur = 0;
//定义拼接多位数的字符串
String str;
//定义字符串中当前坐标的字符
char c;
while (cur < s.length()) {
//判断字符是否是数字,不是则加入list集合中
if (!Character.isDigit(c = s.charAt(cur))) {
infixList.add(c + "");
cur++;
} else {
str = "";
//处理多位数
while (cur < s.length() && Character.isDigit(c = s.charAt(cur))) {
//拼接数字
str += c;
cur++;
}
infixList.add(str);
}
}
return infixList;
}
//将中缀表达式List集合转换成后缀表达式List集合
public static List<String> parseInfixToSuffix(List<String> infixList) {
//定义符号栈
Stack<String> operStack = new Stack<>();
//定义数组存储中间结果
List<String> suffixList = new ArrayList<>();
for (String str : infixList) {
//匹配数字
if (!("+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str) || "(".equals(str) || ")".equals(str))) {
suffixList.add(str);
} else if (str.equals("(")) {
//左括号入栈
operStack.push(str);
} else if (str.equals(")")) {
//弹出符号栈栈顶的运算符,存入list数组,直到遇见左括号,将括号对丢弃
while (!operStack.peek().equals("(")) {
suffixList.add(operStack.pop());
}
//左括号出栈
operStack.pop();
} else {
//匹配符号,处理符号优先级
while (operStack.size() != 0 && getPriority(operStack.peek()) >= getPriority(str)) {
//弹出符号栈栈顶的运算符,存入list数组
suffixList.add(operStack.pop());
}
//符号入栈
operStack.push(str);
}
}
//符号栈中剩余符号出栈
while (operStack.size() != 0) {
suffixList.add(operStack.pop());
}
return suffixList;
}
//根据运算符返回优先级
public static int getPriority(String oper) {
int res = 0;
switch (oper) {
case "+":
res = 1;
break;
case "-":
res = 1;
break;
case "*":
res = 2;
break;
case "/":
res = 2;
break;
default:
System.out.println("不存在该运算符!");
break;
}
return res;
}
}
结果: