栈(stack)栈是一个先入后出的有序列表。
- 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊的线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)
- 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶;而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
栈的应用场景
1.子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
2.处理参数递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
3.表达式的转换【中缀表达式转后缀表达式】和求值。
4.二叉树的遍历
5.图形的深度优先搜索法
栈代码实现(数组):
package test;
import java.util.Scanner;
import javax.management.RuntimeErrorException;
public class StackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(4);
String key = "";
boolean loop = true;
Scanner scanner = new Scanner(System.in);
System.out.println("show:显示栈内数据");
System.out.println("exit:表示退出程序");
System.out.println("push:表示入栈(添加数据)");
System.out.println("pop:表示出栈(取出数据)");
while(loop) {
System.out.print("请输入你的选择(show/exit/push/pop):");
key = scanner.next();
switch (key) {
case "show":
stack.list();
break;
case "push":
System.out.print("请输入一个数字:");
int value = scanner.nextInt();
stack.push(value);
break;
case "pop":
try {
int res = stack.pop();
System.out.printf("出栈的数据是:%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "exit":
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("已退出程序……");
}
}
//创建一个ArrayStack (表示栈 )
class ArrayStack{
private int maxSize;//栈的最大容量
private int[] stack; //数组模拟栈
private int top = -1;//top表示栈顶,初始化为-1
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
/*
* 判断是否栈满
*/
public boolean isFull() {
return top == maxSize - 1;
}
//判断栈是否为空
public boolean isEmpty() {
return top == -1;
}
//入栈
public void push(int value) {
//先判断栈是否满
if(isFull()) {
System.out.println("栈满");
return;
}
top++;
stack[top] = value;//value是入栈的值
}
//出栈
public int pop() {
if(isEmpty()) {
throw new RuntimeException("栈空");//手动抛出异常
}
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("statck["+i+"] = "+stack[i]);
}
}
}
输出结果:
show:显示栈内数据
exit:表示退出程序
push:表示入栈(添加数据)
pop:表示出栈(取出数据)
请输入你的选择(show/exit/push/pop):push
请输入一个数字:9
请输入你的选择(show/exit/push/pop):push
请输入一个数字:6
请输入你的选择(show/exit/push/pop):push
请输入一个数字:5
请输入你的选择(show/exit/push/pop):push
请输入一个数字:2
请输入你的选择(show/exit/push/pop):pop
出栈的数据是:2
请输入你的选择(show/exit/push/pop):show
statck[2] = 5
statck[1] = 6
statck[0] = 9
请输入你的选择(show/exit/push/pop):
栈实现计算器:
代码关键:
1.初始化【数栈】和【符号栈】
2.通过一个index值(索引),遍历表达式
package test;
public class Calculator {
public static void main(String[] args) {
// 测试运算
String s1 = "6+23*2-12+6/2";
String s2 = "4/2*6-3*2-3-49";
String s3 = "2*8*9/3+14-2/2";
String s4 = "45*2/6+21-1";
// 创建两个栈:[数栈]、[符号栈]
ListStack numStack = new ListStack(10);
ListStack opeStack = new ListStack(10);
test(s1, numStack, opeStack);
test(s2, numStack, opeStack);
test(s3, numStack, opeStack);
test(s4, numStack, opeStack);
}
public static void test(String expression, ListStack numStack, ListStack opeStack) {
// 下标索引,用于扫描
int index = 0;
// 将每次扫描得到的char保存到ch
char ch = ' ';
while (true) {
// 依次得到表达式的每一个字符
ch = getCharByIndex(expression, index);
/**
* 判断ch是什么,然后做相应的处理
* 若ch是数字,则存入数栈
* 若是运算符号,则根据不同情况存入符号栈
*/
if (isOperation(ch)) {
// 运用管道过滤器风格,处理运算符
operationSolve1(ch, numStack, opeStack);
} else {
// 数直接入数栈,对值为ASCII值-48
// 当处理多位数时候,不能立即入栈,可能是多位数,调用过滤器处理多位数
index = numSolve1(expression, index, numStack);
}
// 让index+1,并判断是否扫描到expression最后
index++;
if (index >= expression.length()) {
break;
}
}
// 最后只剩下两个数和一个运算符
int res = cal((int) numStack.pop(), (int) numStack.pop(), (char) opeStack.pop());
System.out.printf("%s = %d\n", expression, res);
}
// 获取表达式的下标位置为index的字符
public static char getCharByIndex(String expression, int index) {
return expression.charAt(index);
}
//处理数字入栈的情况,包含处理多位数的情况,并且返回到操作表达式当前的下标
public static int numSolve1(String expression, Integer index, ListStack numStack) {
int end = index + 1;
for (; end < expression.length(); end++) {
char ch = getCharByIndex(expression, end);
// 判断是不是数字
if (!isOperation(ch)) {
continue;
} else {
break;
}
}
String numStr = expression.substring(index, end);
// 数据入栈
numStack.push(Integer.valueOf(numStr));
// 因为test函数进行了+1,所以这里进行-1,避免给重复添加
return end - 1;
}
//符号过滤器1,判断当前是否具有字符
public static void operationSolve1(char ch, ListStack numStack, ListStack opeStack) {
// 判断当前符号栈是否具有操作符
if (!opeStack.isEmpty()) {
operationSolve2(ch, numStack, opeStack);
return;
} else {
opeStack.push(ch);
return;
}
}
// 符号过滤器2,处理字符优先级,递归调用过滤器1
public static void operationSolve2(char ch, ListStack numStack, ListStack opeStack) {
// 比较优先级
if (priority(ch) <= priority((Character) opeStack.peek())) {
// 调用过滤器3进行计算
operationSolve3(numStack,opeStack);
// 递归调用过滤器1,不能递归调用过滤器2,因为可能存在当前运算符栈为空的情况
operationSolve1(ch, numStack, opeStack);
return;
} else {
// 直接将运算符加入到运算符栈中
opeStack.push(ch);
return;
}
}
//符号过滤器3,进行运算
public static void operationSolve3(ListStack numStack, ListStack opeStack) {
// 定义相关变量
int num1 = (int) numStack.pop();
int num2 = (int) numStack.pop();
char operation = (char) opeStack.pop();
int res = cal(num1, num2, operation);
// 把运算结果加到数栈
numStack.push(res);
return;
}
//返回运算符的优先级,数字越大,运算符越高
public static int priority(char operation) {
if (operation == '*' || operation == '/') {
return 1;
} else if (operation == '+' || operation == '-') {
return 0;
} else {
// 假设目前的表达式只有 + - * /
return -1;
}
}
//判断是不是运算符
public static boolean isOperation(char val) {
return val == '+' || val == '-' || val =='*' || val == '/';
}
//计算最后的结果
public static int cal(int num1, int num2, char operation) {
// 用于存放运算的结果
int res = 0;
switch (operation) {
case '+':
res = num1 + num2;
break;
case '-':
// num1是先弹出来的数,为被减数
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
// num1是先弹出来的数,为被除数
res = num2 / num1;
default:
break;
}
return res;
}
}
//表示链表的一个节点
class Node1{
Object element;
Node1 next;
public Node1(Object element) {
this(element, null);
}
/**
* 头插法插入节点
* element:新增节点的value
* n:原来的头节点
*/
public Node1(Object element, Node1 n) {
this.element = element;
next = n;
}
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
public Node1 getNext() {
return next;
}
public void setNext(Node1 next) {
this.next = next;
}
}
//用链表实现堆栈
class ListStack {
Node1 header;//栈顶元素
int elementCount;//栈内元素个数
int size;//栈的大小
/**
* 构造函数,构造一个空的堆栈
*/
public ListStack() {
header = null;
elementCount = 0;
size = 0;
}
//通过构造器 自定义栈的大小
public ListStack(int size) {
header = null;
elementCount = 0;
this.size = size;
}
public void setSize(int size) {
this.size = size;
}
public void setHeader(Node1 header) {
this.header = header;
}
public int getSize() {
return size;
}
public int getElementCount() {
return elementCount;
}
public boolean isEmpty() {
if (elementCount == 0) {
return true;
}
return false;
}
public boolean isFull() {
if (elementCount == size) {
return true;
}
return false;
}
//入栈
public void push(Object value) {
if (this.isFull()) {
throw new RuntimeException("Stack is Full");
}
header = new Node1(value, header);
elementCount++;
}
//出栈
public Object pop() {
if (this.isEmpty()) {
throw new RuntimeException("Stack is empty");
}
Object obj = header.getElement();
header = header.getNext();
elementCount--;
return obj;
}
//返回栈顶元素
public Object peek() {
if (this.isEmpty()) {
throw new RuntimeException("Stack is empty");
}
return header.getElement();
}
}
中缀表达式的求值是平常最为熟悉的,但是对计算机说却不好操作(上述代码有些冗杂)。因此,在计算结束时,往往会将中缀表达式转成其它表达式来操作(一般是转成后缀表达式)。
后缀表达式(逆波兰表达式)
运算符位于操作数之后。
例如: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –
以(3+4)×5-6 为例,用缀表达式求值步骤如下:
1.对应的后缀表达式就是 3 4 + 5 × 6 -
2.从左至右扫描,将3和4压入堆栈
3.遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈
4.将5入栈
5.接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈
6.将6入栈
7.最后是-运算符,计算出35-6的值,即29,由此得出最终结果
代码:
package test;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class Calculator2 {
public static void main(String[] args) {
String suffixExpression = "3 4 + 5 * 6 -";//逆波兰表达式
// 先将3 4 + 5 * 6 - 放入一个链表,配合栈完成计算
List<String> listString = getListString(suffixExpression);
System.out.println(suffixExpression+" = "+calculate(listString));
}
/**
* 将逆波兰表达式的数据和运算符依次放到ArrayList中
* suffixExpression 逆波兰表达式
*/
public static List<String> getListString(String suffixExpression) {
// 将suffixExpression分割
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
for (String element : split) {
list.add(element);
}
return list;
}
/**
* 计算逆波兰表达式最终结果
* stringList 数据和运算符链表
*/
public static int calculate(List<String> stringList) {
// 创建一个栈即可
Stack<String> stack = new Stack<>();
// 遍历链表
for (String element : stringList){
// 使用正则表达式来取出数,匹配多位数
if (element.matches("\\d+")) {
// 入栈
stack.push(element);
} else {
// pop出两个数,并进行运算再入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (element.equals("+")) {
res = num1 + num2;
} else if (element.equals("-")) {
res = num1 - num2;
} else if (element.equals("*")) {
res = num1 * num2;
} else if (element.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
// 把res入栈
stack.push(res + "");
}
}
return Integer.valueOf(stack.pop());
}
}
输出结果: