栈
举例:计算表达式结果
7 * 2 * 2 - 5 + 1 - 5 + 3 -3
1、基本概念
栈 stack 先入后出(FILO first in last out)的有序列表
栈的插入和删除只能在线性表的同一端进行
栈顶:变化端(允许插入和删除) 栈底:固定端
出栈 pop、入栈 push
2、栈的应用场景
- 子程序的调用
- 递归调用
- 表达式转换(中缀表达式转后缀表达式)与求值
- 二叉树遍历
- 图的深度优先搜索算法 depth-first
3、实现栈的思路
- 使用数组实现栈
- 定义一个 top 表示栈顶,初始化 top=-1
- 入栈操作,top++;stack[top]=data;
- 出栈操作,data=stack[top];top--;
4、代码实现栈
java
package com.demo.stack;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(5);
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());
stack.list();
}
}
class ArrayStack{
private int maxSize; // 栈容量
private int top = -1; // 栈顶指针
private int[] arr; // 栈容器
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
this.arr = new int[this.maxSize];
}
// 判满
public boolean isFull(){
return this.top == this.maxSize - 1;
}
//判空
public boolean isEmpty(){
return this.top == -1;
}
// 入栈
public boolean push(int data){
if(this.isFull()){
return false;
}
this.top++;
this.arr[this.top] = data;
return true;
}
// 出栈
public int pop(){
if(isEmpty()){
throw new RuntimeException("stack is empty");
}
int data = arr[top];
top--;
return data;
}
// 显示栈内数据
public void list(){
if(isEmpty()){
System.out.println("stack is empty");
return;
}
int index = top;
while (index > -1){
System.out.printf("arr[%d] = %d\n", index, arr[index]);
index--;
}
}
}
5、交互测试
java
package com.demo.stack;
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(5);
Scanner scanner = new Scanner(System.in);
String key = ""; // 接收输入值
boolean loop = true; // 控制退出菜单
while (loop) {
System.out.println("show: 显示栈信息");
System.out.println("exit: 退出程序");
System.out.println("push: 入栈");
System.out.println("pop: 出栈");
key = scanner.nextLine();
switch (key) {
case "show":
stack.list();
break;
case "push":
int data = scanner.nextInt();
stack.push(data);
break;
case "pop":
try {
System.out.println(stack.pop());
} catch (Exception e){
System.out.println(e.getMessage());
}
break;
case "exit":
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("bye~");
}
}
6、用链表模拟栈
java
package com.demo.stack;
public class LinkedListStackDemo {
public static void main(String[] args) {
LinkedListStack stack = new LinkedListStack();
stack.add(1);
stack.add(2);
stack.add(3);
stack.add(4);
stack.add(5);
stack.list();
System.out.println(stack.pop());
System.out.println(stack.pop());
stack.list();
}
}
class LinkedListStack {
private StackNode top;
// 链表无限扩充不会满
// public boolean isFull(){}
// 判空
public boolean isEmpty() {
return top == null;
}
// 出栈
public int pop() {
if (isEmpty()) {
throw new RuntimeException("stack is empty");
}
int data = top.data;
top = top.next;
return data;
}
// 入栈
public void add(int data) {
StackNode node = new StackNode(data);
node.next = top;
top = node;
}
// 显示栈数据
public void list() {
StackNode temp = top;
while (temp != null) {
System.out.println(temp);
temp = temp.next;
}
}
}
class StackNode {
public int data;
public StackNode next;
public StackNode(int data) {
this.data = data;
}
@Override
public String toString() {
return "StackNode{" +
"data=" + data +
'}';
}
}
使用栈完成表达式计算
思路:
- 通过index索引遍历表达式
- 如果是数字,直接入数字栈
- 如果是符号,分以下情况 3.1. 如果当前符号栈为空,直接入栈 3.2. 如果符号栈有操作符,就进行优先级比较: - 如果当前操作符的优先级小于或等于栈中的操作符,就从数栈中pop弹出两个数,从符号栈中弹出一个操作符,进行运算,计算结果入数栈,将当前操作符入符号栈 - 如果当前操作符的优先级大于栈中的操作符,直接入符号栈
- 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数字和符号,并运算
- 最后在数栈中只有一个数字,就是表达式的结果
验证:
3+2*6-2=13
数字栈:3
符号栈:
数字栈:3
符号栈:+
数字栈:3 2
符号栈:+
数字栈:3 2
符号栈:+ *
数字栈:3 2 6
符号栈:+ *
数字栈:3 2 6
符号栈:+ *
=》2 * 6 = 12
数字栈:3 12
符号栈:+ -
数字栈:3 12 2
符号栈:+ -
=》12 - 2 = 10
数字栈:3 10
符号栈:+
=》 3 + 10 = 13
数字栈:13
符号栈:
=》13
代码实现
java
package com.demo.stack;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CalculatorStackDemo {
public static void main(String[] args) {
// 数字栈和符号栈
CalculatorStack<Integer> numberStack = new CalculatorStack<>();
CalculatorStack<String> operatorStack = new CalculatorStack<>();
String expression = "3+2*6-2";
// 遍历表达式
int index = 0;
while (index < expression.length()) {
String op = expression.substring(index, index + 1);
System.out.println(op);
// 处理操作符
if (CalculatorUtil.isOperator(op)) {
// 操作符栈为空直接入栈
if (operatorStack.isEmpty()) {
operatorStack.push(op);
}
// 如果当前操作符的优先级小于或等于栈中的操作符,
// 就从数栈中pop弹出两个数,从符号栈中弹出一个操作符,进行运算,
// 计算结果入数栈,将当前操作符入符号栈
else if (CalculatorUtil.priority(op) <= CalculatorUtil.priority(operatorStack.peek())) {
int num1 = numberStack.pop();
int num2 = numberStack.pop();
String stackOp = operatorStack.pop();
int ret = CalculatorUtil.calculate(num2, stackOp, num1);
numberStack.push(ret);
operatorStack.push(op);
}
// 如果当前操作符的优先级大于栈中的操作符,直接入符号栈
else {
//可能是多位数
operatorStack.push(op);
}
}
// 处理数字,数字直接入栈
else {
numberStack.push(Integer.parseInt(op));
}
index++;
}
// 清空符号栈
while (!operatorStack.isEmpty()){
int num1 = numberStack.pop();
int num2 = numberStack.pop();
String stackOp = operatorStack.pop();
int ret = CalculatorUtil.calculate(num2, stackOp, num1);
numberStack.push(ret);
}
System.out.println(numberStack.pop());
}
}
class CalculatorUtil {
// 判断是否为操作符,只支持简单的四则运算
public static boolean isOperator(String c) {
List<String> list = Arrays.asList("+", "-", "*", "/");
return list.indexOf(c) > -1;
}
// 判断优先级
public static int priority(String operator) {
Map<String, Integer> map = new HashMap<>();
map.put("*", 1);
map.put("/", 1);
map.put("+", 0);
map.put("-", 0);
return map.getOrDefault(operator, -1);
}
// 计算
public static int calculate(int num1, String operator, int num2) {
int ret;
switch (operator) {
case "+":
ret = num1 + num2;
break;
case "-":
ret = num1 - num2;
break;
case "*":
ret = num1 * num2;
break;
case "/":
ret = num1 / num2;
break;
default:
throw new RuntimeException("don't parse operator");
}
return ret;
}
}
class CalculatorStack<T> {
private CalculatorNode<T> top;
// 链表无限扩充不会满
// public boolean isFull(){}
// 判空
public boolean isEmpty() {
return top == null;
}
// 出栈
public T pop() {
if (isEmpty()) {
throw new RuntimeException("stack is empty");
}
T data = top.data;
top = top.next;
System.out.println("pop: " + data);
return data;
}
// 入栈
public void push(T data) {
CalculatorNode<T> node = new CalculatorNode<>(data);
node.next = top;
top = node;
System.out.println("push: " + data);
}
// 栈顶数据
public T peek() {
if (isEmpty()) {
return null;
} else {
return top.data;
}
}
// 统计栈内数据个数
public int length() {
// 显示栈数据
CalculatorNode<T> temp = top;
int count = 0;
while (temp != null) {
count++;
temp = temp.next;
}
return count;
}
// 显示栈数据
public void list() {
CalculatorNode<T> temp = top;
while (temp != null) {
System.out.println(temp);
temp = temp.next;
}
}
}
class CalculatorNode<T> {
public T data;
public CalculatorNode<T> next;
public CalculatorNode(T data) {
this.data = data;
}
@Override
public String toString() {
return "StackNode{" +
"data=" + data +
'}';
}
}
前缀、中缀、后缀表达式
前缀表达式(波兰表达式) 后缀表达式(逆波兰表达式)
举例:
(3+4)x5-6
前缀表达式
-x+ 3 4 5 6