基本介绍
- 栈的英文为(stack)
- 栈是一个先入后出(FILO-First In Last Out)的有序列表。
- 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
- 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
- 图解方式说明**出栈(pop)和入栈(push)**的概念
应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
- 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
- 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
- 二叉树的遍历。
- 图形的深度优先(depth一 first)搜索法。
快速入门
用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来储存栈的数据内容,下面我们就用数组模拟栈的出栈,入栈等操作。
思路分析
- 使用数组来模拟栈
- 定义一个top来表示栈顶,初始化为 -1
- 入柱的操作:当有数据加入到栈时,top++; stack[top] =data;
- 出栈的操作:int value = stack[top]; top–; return value;
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104package com.ysy.stack;
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
//测试栈
ArrayStack stack = new ArrayStack(4);
String key = "";
boolean loop = true;//控制是否退出菜单
Scanner sc = new Scanner(System.in);
while (loop){
System.out.println("show: 表示显示栈");
System.out.println("exit: 表示退出栈");
System.out.println("push: 表示入栈");
System.out.println("pop: 表示出栈");
System.out.println("请输入你的选择~");
key = sc.next();
switch (key){
case "show":
stack.list();
break;
case "push":
System.out.println("请输入一个数~");
int i = sc.nextInt();
stack.push(i);
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":
sc.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;
}
//出栈
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.printf("stack[%d]=%d\n", i, stack[i]);
}
}
}
栈实现综合计算器(中缀表达式)
- 就是最常见的运算表达式,如:(3 + 4) × 5 - 6
- 中缀表达式求值对计算机来说不好操作,因此在计算时往往转换为后缀表达式
请问:计算机底层是如何运算得到结果的? 注意不是简单的把算式列出运算,因为我们看这个算式7×2×2-5+1-5×3-3,但是计算机怎么理解这个算式的(对计算机而言,它接收到的就是一个字符串),我们讨论的是这个问题
思路分析
通过一个index值《索引》,来遍历我们的表达式
如果我们发现是一个数字,就直接入数栈
- 当处理多位数时,需要向表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈
如果发现扫描到是一个符号,就分如下情况
如果发现当前的符号栈为空,就直接入栈
如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈.
当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行.
最后在数栈只有一个数字,就是表达式的结果
代码实现
1 | package com.ysy.stack; |
前缀表达式(波兰表达式)
- 前缀表达式的运算符位于操作数之前
- 前缀表达式的计算机求值
- 从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
- 例如:
(3 + 4) × 5 - 6
对应的前缀表达式为- × + 3 4 5 6
,求值步骤如下- 从右至左扫描,将6、5、4、3压入堆栈
- 遇到 + 运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出 3 + 4 的值,得7, 再将7入栈
- 接下来是 × 运算符,因此弹出7和5,计算出7 × 5 = 35,将35入栈
- 最后是 - 运算符,计算出35 - 6的值,即29,由此得出最终结果
后缀表达式(逆波兰表达式)
后缀表达式的运算符位于操作数之后
后缀表达式的计算机求值
- 从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素和栈顶元素〉,并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
- 例如:
(3 + 4) × 5 - 6
对应的后缀表达式就是3 4 + 5 × 6 -
,求值步骤如下- 从左至右扫描,将3和4压入堆栈;
- 遇到 + 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
- 将5入栈;
- 接下来是 × 运算符,因此弹出5和7,计算出7×5=35,将35入栈;
- 将6入栈;
- 最后是 - 运算符,计算出35-6的值,即29,由此得出最终结果
逆波兰计算器
- 输入一个逆波兰表达式,使用栈计算其结果
1 | package com.ysy.stack; |
中缀表达式转后缀表达式
思路
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较;
- 遇到括号时:
- 如果是左括号“(”,则直接压入s1
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
- 重复步骤2至5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
实例
将中缀表达式
1 + ((2 + 3) × 4) - 5
转换为后缀表达式的过程如下
代码实现
1 | package com.ysy.stack; |