Appearance
栈
基本介绍(stack)
- 先进后出
- 插入/删除只能在线性表的同一端进行
- 栈顶:允许插入和删除的一端,栈底:另一端
应用场景
- 子程序的调用
- 在调往子程序之前,先将下一个指令的地址存到堆栈中,直到子程序执行完再将地址取出,以回到原来的程序中
- 处理递归调用
- 与子程序调用类似,将下一指令的地址、参数、区域变量等数据存入堆栈中。
- 表达式的转换【中缀表达式转后缀表达式】与求值(实际解决)
- 二叉树的遍历
- 图形的深度优先算法
思路分析
- 数组模拟栈
- 定义top来表示栈顶,初始化为-1
- 入栈,当有数据加入到栈时,top++,stack[top] = data
- 出栈,value = stack[top];top--;return value
代码实现
class ArrayStack {
private final int maxSize; //栈的大小
private final int[] stack; //存储数据
private int top = -1; //栈顶
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
public boolean isFull() {
return top == maxSize - 1;
}
public boolean isEmpty() {
return top == -1;
}
public void push(int val) {
if (isFull()) {
System.out.println("栈满");
return;
}
stack[++top] = val;
}
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空");
}
return stack[top--];
}
public void list() {
if (isEmpty()) {
System.out.println("栈空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.printf("%d\t", stack[i]);
}
System.out.println();
}
}
拓展
完成表达式计算(中缀表达式)
- 通过index来遍历我们的表达式
- 发现是一个数字,直接进数栈
- 发现是符号
- 符号栈为空,直接进栈
- 符号栈不为空
- 如果当前操作符的优先级小于或等于栈中的操作符,则pop出两个数和一个符号,计算,将结果和当前的操作符压入栈
- 反之直接入栈
- 当表达式扫描完毕,顺序的从数栈和符号栈中pop出数据,并运行。
- 最后在数栈只有一个数字,就是表达式的结果
存在问题
- 多位数计算
- 小括号
public static void main(String[] args) {
String expression = "3+2*5-4";
CalculatorStack numStack = new CalculatorStack(10);
CalculatorStack operaStack = new CalculatorStack(10);
int index = 0; //扫描
int num1 = 0;
int num2 = 0;
int operator = ' ';
int res = 0;
char ch = ' '; //每次扫描的结果
//扫描表达式
while (true) {
ch = expression.substring(index, index + 1).charAt(0);
if (operaStack.isOperator(ch)) {
if (!operaStack.isEmpty()) {
if (operaStack.priority(operaStack.getTop()) >= operaStack.priority(ch)) {
num1 = numStack.pop();
num2 = numStack.pop();
operator = operaStack.pop();
res = operaStack.cal(num1, num2, (char) operator);
numStack.push(res);
}
operaStack.push(ch);
} else {
operaStack.push(ch);
}
} else {
keepNum += ch;
if (index == expression.length() - 1) {
numStack.push(ch - 48);
} else {
if (operaStack.isOperator(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
keepNum = "";
}
}
}
index++;
if (index >= expression.length()) {
break;
}
}
//计算表达式
while (true) {
if (operaStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
operator = operaStack.pop();
res = operaStack.cal(num1, num2, (char) operator);
numStack.push(res);
}
System.out.printf(expression + " = " + numStack.pop());
}
class CalculatorStack {
private final int maxSize; //栈的大小
private final int[] stack; //存储数据
private int top = -1; //栈顶
public CalculatorStack(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
public int priority(char operator) {
if (operator == '*' || operator == '/') {
return 1;
} else if (operator == '+' || operator == '/') {
return 0;
} else {
return -1;
}
}
//是否是操作符
public boolean isOperator(int val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
//计算方法
public int cal(int num1, int num2, char operator) {
int res = 0;
switch (operator) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1; //注意数据
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;//注意顺序
break;
default:
break;
}
return res;
}
public boolean isFull() {
return top == maxSize - 1;
}
public boolean isEmpty() {
return top == -1;
}
public void push(int val) {
if (isFull()) {
System.out.println("栈满");
return;
}
stack[++top] = val;
}
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空");
}
return stack[top--];
}
public char getTop() {
return (char) stack[top];
}
public void list() {
if (isEmpty()) {
System.out.println("栈空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.printf("%d\t", stack[i]);
}
System.out.println();
}
}
前缀表达式
计算机 从右到左,将数字压入栈,遇到运算符时,弹出栈顶和次顶元素,计算结果,重新入栈,重复过程。
后缀表达式(逆波兰表达式)
从左到右,遇到数字,压入栈,遇到运算符,弹出栈顶和次顶元素,计算结果,重复过程。
后缀表达式计算
public class PolandNotation {
public static void main(String[] args) {
String suffixExpression = "30 4 + 5 * 6 -";
List<String> list = getListString(suffixExpression);
int res = calculator(list);
System.out.println(res);
}
public static List<String> getListString(String suffixExpression) {
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
for (String ele : split) {
list.add(ele);
}
return list;
}
public static int calculator(List<String> list) {
Stack<String> stack = new Stack<>();
for (String e : list) {
if (e.matches("\\d+")) {
//匹配多位数
stack.push(e);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (e.equals("+")) {
res = num1 + num2;
} else if (e.equals("-")) {
res = num1 - num2;
} else if (e.equals("*")) {
res = num1 * num2;
} else if (e.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
stack.push("" + res);
}
}
return Integer.parseInt(stack.pop());
}
}
中缀表达式转后缀表达式
- 使用两个栈,运算符栈s1 和 存储中间结果的栈s2
- 从左到右扫描中缀表达式
- 遇到操作数,压入s2
- 遇到运算符,比较和s1栈顶的优先级
- 如果s1为空或者为左括号,直接入栈
- 优先级比栈顶高,入栈
- 优先级比栈顶低,弹出,压入s2,随后继续比较新的栈顶
- 遇到括号时
- 左括号,直接压入s1
- 右括号,依次弹出s1栈顶,压入s2,知道遇到左括号,括号不入栈
- 继续扫描,直到表达式的最右边
- 将s1中剩余的运算符弹出,并压入s2
- 依次弹出s2输出,结果的逆序就是中缀转后缀的结果
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
List<String> InfixExpressList = toInfixExpression("1+((2+5)*6+1)*4");
System.out.println(InfixExpressList);
List<String> parseSuffixExpression = parseSuffixExpressList(InfixExpressList);
System.out.println(parseSuffixExpression);
System.out.println(calculator(parseSuffixExpression));
}
//转化为中缀数组
public static List<String> toInfixExpression(String s) {
List<String> list = new ArrayList<>();
int i = 0; // 指针
String str; //对多位数拼接
char c;
do {
//不是数字
if (!isNumber(c = s.charAt(i))) {
list.add(c + "");
i++;
} else {
//数字
str = "";
while (i < s.length() && isNumber(c = s.charAt(i))) {
str += c;
i++;
}
list.add(str);
}
} while (i < s.length());
return list;
}
public static boolean isNumber(char ch) {
return ch >= 48 && ch <= 57;
}
public static List<String> getListString(String suffixExpression) {
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
for (String ele : split) {
list.add(ele);
}
return list;
}
//计算后缀数组
public static int calculator(List<String> list) {
Stack<String> stack = new Stack<>();
for (String e : list) {
if (e.matches("\\d+")) {
//匹配多位数
stack.push(e);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (e.equals("+")) {
res = num1 + num2;
} else if (e.equals("-")) {
res = num1 - num2;
} else if (e.equals("*")) {
res = num1 * num2;
} else if (e.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
stack.push("" + res);
}
}
return Integer.parseInt(stack.pop());
}
public static int priority(char operator) {
if (operator == '*' || operator == '/') {
return 1;
} else if (operator == '+' || operator == '-') {
return 0;
} else {
return -1;
}
}
//中缀转化后缀数组
public static List<String> parseSuffixExpressList(List<String> list) {
Stack<String> stack1 = new Stack<>();
//因为整个过程中 s2没有pop操作,而且后续需要对其逆序输出,所以我们不用stack,直接使用list
List<String> stack2 = new ArrayList<>();
char c;
for (String e : list) {
if (e.matches("\\d+")) {
//数字
stack2.add(e);
} else if (e.equals("(")) {
//左括号
stack1.push(e);
} else if (e.equals(")")) {
//右括号
while (!stack1.peek().equals("(")) {
stack2.add(stack1.pop());
}
stack1.pop(); //消除“(”
} else {
//普通符号
while (stack1.size() != 0 && priority(e.charAt(0)) <= priority(stack1.peek().charAt(0))) {
stack2.add(stack1.pop());
}
stack1.push(e);
}
}
//将s1剩余的符号依次弹出压入s2
while (stack1.size() != 0) {
stack2.add(stack1.pop());
}
return stack2;
}
}