栈
栈的介绍
1) 栈的英文为(stack)
2) 栈是一个先入后出(FILO-First In Last Out)的有序列表。
3) 栈(stack)是 限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的 一端为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
4) 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
5) **图解方式说明出栈(pop)和入栈(push)**的概念
栈的应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以
回到原来的程序中。
- 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆
栈中。
-
表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
-
二叉树的遍历。
-
图形的深度优先(depth 一 first)搜索法。
栈入门
- 用数组模拟栈的使用,由于栈是一种有序列表,可以使用数组的结构来存储栈的数据内容
- 实现思路,示意图如下:
package com.romanticlei.stack;
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
// 创建一个ArrayStack 对象
ArrayStack stack = new ArrayStack(4);
String key = "";
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("show:表示显示栈");
System.out.println("exit:表示退出栈");
System.out.println("push:表示数据入栈");
System.out.println("pop:表示数据出栈");
System.out.println("请输入您的选择");
key = scanner.next();
switch (key){
case "show":
stack.list();
break;
case "push":
System.out.println("请输入一个数:");
int val = scanner.nextInt();
stack.push(val);
break;
case "pop":
int value = stack.pop();
System.out.println("出栈的数据为:" + value);
break;
case "exit":
System.exit(-1);
break;
default:
System.out.println("输入有误!");
break;
}
}
}
}
class ArrayStack {
private int maxSize; // 栈的大小
private int[] stack; // 数组,数组模拟栈,数据就放在该数组中
private int top = -1; // top 表示栈顶,初始化为-1
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
// 判断栈空
public boolean isEmpty() {
return top == -1;
}
// 判断栈满
public boolean isFull() {
return top == maxSize - 1;
}
// 入栈
public void push(int value) {
// 判断是否栈满
if (isFull()) {
System.out.println("栈已满!");
return;
}
top++;
stack[top] = value;
}
// 出栈
public int pop(){
if (isEmpty()){
System.out.println("栈已空");
return -1;
}
int value = stack[top];
top--;
return value;
}
// 遍历栈,遍历时,需要从栈顶开始显示数据
public void list(){
if (isEmpty()){
System.out.println("栈已空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.println("出栈数据为stack[" + i + "] = " + stack[i]);
}
}
}
show:表示显示栈
exit:表示退出栈
push:表示数据入栈
pop:表示数据出栈
请输入您的选择
逆波兰计算器完整版
完整版的逆波兰计算器,功能包括:
支持 + - * / ( )
多位数,支持小数,
兼容处理, 过滤任何空白字符,包括空格、制表符、换页符
package com.romanticlei.stack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;
public class PolandNotation {
public static void main(String[] args) {
// 完成一个中缀表达式转集合
/**
* 直接将字符串转成后缀表达式不方便,素偶一我们需要将字符串先转成list
* 例如:1+((2+3)x4)-5 => 转成 [1, +, (, (, 2, +, 3, ), x, 4, ), -, 5]
*/
String expreesion = "1+ ((21.53+ 3)* " + "\t\n" + "4)-5";
expreesion = replaceAllBlank(expreesion);
List<String> infixExpressionList = toInfixExpressionList(expreesion);
System.out.println("中缀表达式转集合之后:" + infixExpressionList);
/**
* 将得到的中缀表达式list => 转成后缀表达式的list
*/
List<String> parseSuffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
System.out.println("后缀表达式对应的集合:" + parseSuffixExpreesionList);
System.out.println("中缀表达式转后缀表达式计算结果~: " + calculate(parseSuffixExpreesionList));
System.out.println("/* -------------------------------------------------------------*/");
/* -------------------------------------------------------------*/
// 先定义一个逆波兰表达式
// (3+4)x5-6 => 3 4 + 5 x 6 -
// 说明:为了方便,逆波兰表达式和符号使用空格隔开
String suffixExpression = "3 4 + 5 * 6 -";
// 思路
// 先将 "3 4 + 5 x 6 -" => 放到 ArrayList中
// 将 ArrayList 传递给一个方法,然后遍历 ArrayList 配合栈完成计算
System.out.println(getListString(suffixExpression));
double res = calculate(getListString(suffixExpression));
System.out.println("计算结果为: " + res);
}
// 将需要转换的字符串过滤,去掉空白符等
public static String replaceAllBlank(String expression) {
// \\s+ 匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]
return expression.replaceAll("\\s+", "");
}
// 将中缀表达式转成集合的形式
public static List<String> toInfixExpressionList(String expression) {
int i = 0; // 定义索引
String str;
char ch = ' ';
List<String> list = new ArrayList<>();
do {
// 第一位不是数字时直接抛出异常
if (i == 0) {
ch = expression.charAt(i);
try {
Integer.parseInt(String.valueOf(ch));
} catch (Exception e) {
throw new RuntimeException("data illeagle,start not with a number");
}
}
if ((ch = expression.charAt(i)) < 48 || (ch = expression.charAt(i)) > 57) {
// 符号直接入栈
list.add(String.valueOf(ch));
i++;
} else {
str = "";
while (i < expression.length() && (ch = expression.charAt(i)) >= 48 && (ch = expression.charAt(i)) <= 57) {
// 如果是数字则判断下一位是否依旧是数字,然后进行数字拼接操作
str += ch;
i++;
}
// 将拼接后的数字入栈
list.add(str);
}
} while (i < expression.length());
return list;
}
// 中缀表达式集合转后缀表达式集合
public static List<String> parseSuffixExpreesionList(List<String> list) {
// 符号栈
Stack<String> s1 = new Stack<>();
// 后缀表达式集合
List<String> s2 = new ArrayList<>();
for (String item : list) {
// 匹配多位数字入栈
if (item.matches("\\d+")) {
s2.add(item);
} else if ("(".equals(item)) {
s1.push(item);
} else if (")".equals(item)) {
// 如果当前为右括号,就匹配到距栈顶最近的一个左括号为止
while (!"(".equals(s1.peek())) {
s2.add(s1.pop());
}
// 如果弹出的是 "(" 那就直接清除 "("
s1.pop();
} else {
// 当 item 的优先级小于等于s1 栈顶运算符,那么就需要将s1栈顶的数据弹出,直到item 运算符优先级高或者s1栈顶为空,item 入栈
while (s1.size() > 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
if (".".equals(s1.peek())) {
int len = s2.size();
String num1 = s2.get(len - 1);
String num2 = s2.get(len - 2);
String symbol = s1.pop();
String newNum = num2 + symbol + num1;
s2.set(len - 2, newNum);
s2.remove(len - 1);
} else {
s2.add(s1.pop());
}
}
// 将 item 放入大栈顶
s1.push(item);
}
}
// 将s1 中剩余的运算符一次弹出并加入到 s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;
}
// 将一个逆波兰表达式,依次将数据和运算符 放入到 ArrayList中
public static List<String> getListString(String suffixExpression) {
String[] split = suffixExpression.split(" ");
List<String> list = Arrays.asList(split);
return list;
}
// 完成对逆波兰表达式的运算
public static double calculate(List<String> list) {
Stack<String> stack = new Stack<>();
// 遍历集合 list
for (String item : list) {
// 判断是否是数字
if (item.matches("^[0-9]+([.]{1}[0-9]+){0,1}$")) {
stack.push(item);
} else {
// int num2 = Integer.parseInt(stack.pop());
// int num1 = Integer.parseInt(stack.pop());.
// int res = 0;
Double num2 = Double.parseDouble(stack.pop());
Double num1 = Double.parseDouble(stack.pop());
double res = 0;
if ("+".equals(item)) {
res = num1 + num2;
} else if ("-".equals(item)) {
res = num1 - num2;
} else if ("*".equals(item)) {
res = num1 * num2;
} else if ("/".equals(item)) {
res = num1 / num2;
} else {
throw new RuntimeException("无法识别的运算符");
}
stack.push(String.valueOf(res));
}
}
// return Integer.parseInt(stack.pop());
return Double.parseDouble(stack.pop());
}
}
// 编写一个类 Operation,可以返回一个运算符对应的优先级
class Operation {
private static int LEFT = 0;
private static int RIGHT = 0;
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
// 判断对应运算符的优先级
public static int getValue(String operation) {
switch (operation) {
case "(":
return LEFT;
case ")":
return RIGHT;
case "+":
return ADD;
case "-":
return SUB;
case "*":
return MUL;
case "/":
return DIV;
default:
// System.out.println("运算符不存在");
return Integer.MAX_VALUE;
}
}
}
中缀表达式转集合之后:[1, +, (, (, 21, ., 53, +, 3, ), *, 4, ), -, 5]
后缀表达式对应的集合:[1, 21.53, 3, +, 4, *, +, 5, -]
中缀表达式转后缀表达式计算结果~: 94.12
/* -------------------------------------------------------------*/
[3, 4, +, 5, *, 6, -]
计算结果为: 29.0