栈 stack
- 一个先入后出的有序列表。
数组实现栈:
package msp.cai.stack;
/*
* 数组实现栈
*/
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(5);
System.out.println("== 入栈 ==");
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
stack.showStack();
System.out.println("== 出栈 ==");
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
}
}
class ArrayStack {
int maxSize; // 最大长度
int top = -1; // 栈顶
int[] arr; // 存放数据
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
arr = new int[this.maxSize];
}
// 判断栈满
public boolean isFull() {
return top==(maxSize-1);
}
// 判断栈空
public boolean isEmpty() {
return top==-1;
}
// 入栈
public void push(int data) {
if (isFull())
return;
arr[++top] = data;
}
// 出栈
public int pop() {
if (isEmpty())
return -1;
return arr[top--];
}
// 打印栈
public void showStack() {
if (isEmpty()) {
System.out.println("栈为空~");
} else {
System.out.println("栈中数据为:");
for (int i = 0; i <= top; i++) {
System.out.printf("%d\t",arr[i]);
}
System.out.println();
}
}
}
栈实现计算器:
思路:
- 使用两个栈,munStack(数字栈) operaStack(符号栈)
- 通过一个index(索引),遍历表达式字符串
- 如果是数字,直接入栈
- 如果是字符:
- 若栈为空,直接push
- 若栈不为空:
- 若当前符号优先级大于栈顶符号的优先级,push
- 否则,从数字栈pop两个数,从符号栈pop运算符,将运算结果push到数字栈,最后将当前符号push到符号栈。
- 当表达式扫描完毕,顺序计算。即pop两个数与一个运算符,将计算结果push到数字栈。
package msp.cai.stack;
/*
* 栈实现表达式计算
*/
public class ArrayStackDemo01 {
public static void main(String[] args) {
// 创建数字栈与符号栈
numStack n_stack = new numStack(100);
operaStack o_stack = new operaStack(50);
String exp = "1+1*8+2*6";
for (int i = 0; i < exp.length(); i++) {
char item = exp.charAt(i);
// 符号
if (item=='+' || item=='-' || item=='*' || item=='/') {
if (o_stack.isEmpty())
o_stack.push(item);
else {
char top_opera = o_stack.getTop();
if ((item=='*' || item=='/') && (top_opera=='+' || top_opera=='-'))
o_stack.push(item);
else {
int num1 = n_stack.pop();
int num2 = n_stack.pop();
char ope = o_stack.pop();
n_stack.push(calculation(num1,num2,ope));
o_stack.push(item);
}
}
}
// 是数字,直接入栈
else {
int num = item - '0';
n_stack.push(num);
}
}
while (!o_stack.isEmpty()) {
int num1 = n_stack.pop();
int num2 = n_stack.pop();
char ope = o_stack.pop();
n_stack.push(calculation(num1,num2,ope));
}
System.out.println("计算结果为: " + n_stack.pop());
}
// 计算方法
public static int calculation(int nim1, int num2, char ope) {
int result = 0;
switch (ope){
case '+':
result = num2 + nim1;
break;
case '-':
result = num2 - nim1;
break;
case '*':
result = num2 * nim1;
break;
case '/':
result = num2 / nim1;
break;
}
return result;
}
}
// 数字栈
class numStack {
private int maxSize;
private int top;
private int[] arr;
public numStack(int maxSize) {
this.maxSize = maxSize;
top = -1;
arr = new int[this.maxSize];
}
// 判断栈满
public boolean isFull() {
return top==(maxSize-1);
}
// 判断栈空
public boolean isEmpty() {
return top==-1;
}
// 入栈
public void push(int data) {
if (isFull())
return;
arr[++top] = data;
}
// 出栈
public int pop() {
if (isEmpty())
return -1;
return arr[top--];
}
}
// 符号栈
class operaStack {
private int maxSize;
private int top;
private char[] arr;
public operaStack(int maxSize) {
this.maxSize = maxSize;
top = -1;
arr = new char[this.maxSize];
}
// 判断栈满
public boolean isFull() {
return top==(maxSize-1);
}
// 判断栈空
public boolean isEmpty() {
return top==-1;
}
// 入栈
public void push(char data) {
if (isFull())
return;
arr[++top] = data;
}
// 出栈
public char pop() {
if (isEmpty())
return '0';
return arr[top--];
}
// 获取栈顶元素
public char getTop() {
if (isEmpty()) {
return '0';
}
return arr[top];
}
}
前缀 中缀 后缀 表达式:
-
前缀
- 从右至左扫描表达式,遇到数字入栈,遇到运算符时,弹出两个数字进行计算,将结果入栈。
- 中缀:(3+4)*5 -6 --> 前缀:- * + 3 4 5 6
-
中缀
- 我们熟知的,但对于计算机来说不好操作。
-
后缀表达式
- 逆波兰表达式,与前缀相似,运算符位于数字后面。
- 从左至右扫描表达式,遇到数字入栈,遇到运算符时,弹出两个数字进行计算,将结果入栈。
- 中缀:(3+4) * 5 - 6 --> 后缀:3 4 + 5 * 6 -
中缀转后缀:
中缀:1 + ((2+3) * 4) - 5
- 初始化两个栈:符号栈s1和中间结果栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数,push到s2;
- 遇到运算符时,
- 若s1为空,或者栈顶为“(”时,直接将此运算符push到s1;
- 否则若优先级高于栈顶运算符,直接将此运算符push到s1;
- 否则,将s1栈顶运算符pop,然后push到s2,返回4.1,重新判断。
- 遇到括号时,
- 左括号“(”,直接push到s1;
- 右括号“)”,依次pop s1中的符号,push到s2中,直到遇到左括号,此时就相当于去掉了一对括号。
- 重复步骤2-5,直至扫描完中最表达式;
- 将s1中剩余运算符依次pop并push到s2
- s2出栈结果的逆序即为后缀表达式。
package msp.cai.stack;
import java.util.ArrayList;
import java.util.Stack;
/*
* 使用栈实现计算表达式:
* 1. 获取表达式字符串
* 2. 中缀 --> 后缀
* 3. 计算结果
*/
public class BolanCalculator {
public static void main(String[] args) {
// 中缀表达式
String midStr = "1 + ( ( 2 + 3 ) * 4 ) - 5";
System.out.println("中缀表达式为: " + midStr);
// 将中缀转后缀 "1 2 3 + 4 * + 5 - "
String rearStr = mid2rear(midStr);
System.out.println("后缀表达式为: " + rearStr);
String[] rearStrArr = rearStr.split(" ");
// 入栈
Stack<String> stack = new Stack<String>();
for (String s : rearStrArr) {
if (!isNotANumber(s))
stack.push(s);
else {
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
switch (s) {
case "+":
stack.push(String.valueOf(num2+num1));
break;
case "-":
stack.push(String.valueOf(num2-num1));
break;
case "*":
stack.push(String.valueOf(num2*num1));
break;
case "/":
stack.push(String.valueOf(num2/num1));
break;
}
}
}
System.out.println("计算结果为: " + stack.pop());
}
// 中缀表达式转后缀表达式
public static String mid2rear(String midStr) {
// 1. 初始化两个栈
Stack<String> s1 = new Stack<String>(); // 符号栈
Stack<String> s2 = new Stack<String>(); // 中间结果栈
String[] str = midStr.split(" ");
// 2. 从左至右依次扫描
for (String s : str) {
// 3. 遇到操作数,push
if (!isNotANumber(s))
s2.push(s);
// 4. 遇到运算符时
else if (!isBracket(s)){
while (true) {
// 4.1 若s1为空,或栈顶为“(”时,直接入栈
if (s1.isEmpty() || s1.peek().equals("(")) {
s1.push(s);
break;
}
// 4.2 当前字符优先级高于栈顶字符,直接入栈
if (getPriority(s) > getPriority(s1.peek())) {
s1.push(s);
break;
}
// 4.3 否则将s1栈顶运算符pop,push到s2,返回4.1
String pop = s1.pop();
s2.push(pop);
}
}
// 5. 遇到括号时
else {
// 5.1 遇到“(”,直接push
if (s.equals("("))
s1.push(s);
// 5.2 依次pop s1中的符号,push到s2中,直到遇到左括号
else {
String pop;
while (!(pop=s1.pop()).equals("("))
s2.push(pop);
}
}
}
// 6. 将s1中剩余运算符依次pop并push到s2
while (!s1.isEmpty())
s2.push(s1.pop());
// 7. 后缀表达式即为s2出栈的逆序
ArrayList<String> rearArr = new ArrayList<String>();
while (!s2.isEmpty()) {
rearArr.add(s2.pop());
}
StringBuilder rearStr = new StringBuilder();
for (int i = rearArr.size()-1; i >= 0; i--) {
rearStr.append(rearArr.get(i)).append(" ");
}
return rearStr.toString();
}
// 判断字符串是否为操作数
public static boolean isNotANumber(String str) {
return str.equals("+") || str.equals("-") || str.equals("*")
|| str.equals("/") || str.equals("(") || str.equals(")");
}
// 判断字符串是否是括号
public static boolean isBracket(String str) {
return str.equals("(") || str.equals(")");
}
// 获取字符的优先级
public static int getPriority(String str) {
if (str.equals("*") || str.equals("/"))
return 1;
else if (str.equals("+") || str.equals("-"))
return 0;
else
return -1;
}
}