栈
栈的一个需求:输入一个表达式722-5+1-5+3-3,实现计算
问题:计算机底层是如何实现?注意不是简单的把算式列出计算,因为我们看这个算式722-5,但是计算机怎么理解这个算式的(对计算机而言,它接受到的就是一个字符串),我们讨论的是这个问题——栈
栈的介绍
- 栈的英文为(stack)
- 栈是一个先入后出(FILO-First in Last Out)的有序列表
- 栈(stack)是限制线性表中元素的插入和删除,只能在线性表的同一端进行的一种特殊线性表,允许插入和删除的一端,为变化的一端,称为栈顶(top),另一端为固定的一端,称为栈底(Bottom)
- 根据栈的定义,最先放入栈中元素在栈底,最后放的元素在栈顶,而删除元素刚好相反,最后放入元素最先删除,最先放入的元素最后删除。
- 图解栈的出栈(pop)和入栈(push)
栈的应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完成后再将地址取出,以回到原来的程序中
- 处理递归的调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数,区域变量等数据存入堆栈中
- 表达式的转换(中缀表达式转后缀表达式)与求值(实际解决)
- 二叉树的遍历
- 图形的深度优先(depth-first)搜索法
栈的快速入门
数组模拟栈的思路分析:
- 使用数组来模拟栈
- 定义一个top来表示栈顶
- 入栈的操作,当有数据加入到栈时,top++;stack[top]=data
- 出栈的操作,int value = stack[top]; top–; return value;
思路分析图如下:
代码实现如下:
package com.stack;
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
// 测试一下
ArrayStack stack = new ArrayStack(4); // 创建一个ArrayStack的对象
String key = "";
boolean loop = true; // 控制是否退出菜单
Scanner scanner = 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 = scanner.next();
switch (key) {
case "show":
stack.list();
break;
case "push":
System.out.println("请输入一个数");
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("程序退出");
}
}
// 定义一个类表示栈结构
class ArrayStack {
private int maxSize; // 栈的大小
private int[] stack; // 定义一个数组,用来模拟栈,数据就存放在该数组中
private int top = -1; // top表示栈顶,初始化为-1
// 构造函数
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.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]);
}
}
}
运行结果如下:
练习:使用双链表来实现栈
思路分析:
- 先定义一个双链表结点类
- 再定义一个双链表类来管理并实现链表栈的push,pop,list
注意:采用双链表是为了实现链表的逆序显示来达到栈的逆序显示的目的
代码实现如下:
package com.stack;
import java.util.Scanner;
public class LinkedListStackDemo {
public static void main(String[] args) {
// 测试
SingleLinkedList listStack = new SingleLinkedList();
String key = "";
boolean loop = true; // 控制是否退出菜单
Scanner scanner = 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 = scanner.next();
switch (key) {
case "show":
listStack.list();
break;
case "push":
System.out.println("请输入一个数");
int value = scanner.nextInt();
Node node = new Node(value);
listStack.push(node);
break;
case "pop":
try {
int res = listStack.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("程序退出");
}
}
// 定义SingleLinkedList管理我们的单链表栈
class SingleLinkedList {
// 初始化一个头结点
private Node head = new Node(0);
// 返回头结点
public Node getHead() {
return head;
}
public boolean isEmpty() { // 判断栈是否空
Node temp = head;
if (temp.next == null) {
return true;
}
return false;
}
// 添加结点入栈
public void push(Node node) {
Node temp = head;
while (true) {
if (temp.next == null) { // 遍历找到最后一个结点
break;
}
temp = temp.next;
}
temp.next = node; // 添加结点入栈
node.pre = temp;
}
// 删除结点出栈
public int pop() {
Node temp = head;
if (isEmpty()) {
// 栈空抛出异常来处理
throw new RuntimeException("栈空,无数据");
}
while (true) {
if (temp.next.next == null) { // 找到最后一个结点的前一个结点,删除最后一个结点并输出它
int x = temp.next.no; // 定义临时辅助变量x用来保存并返回待删除的结点的数字
temp.next = temp.next.next;
return x;
}
temp = temp.next; // temp向后移动
}
}
// 显示栈的情况
public void list() {
Node temp = head.next;
if (isEmpty()) {
System.out.println("栈空,无数据");
return;
}
else {
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
} //while结束后temp指针指向最后一个结点
while(true) {
if (temp == head) {
break;
}
System.out.println(temp);
temp = temp.pre; //指针向前移动
}
}
}
}
// 定义每个栈的结点
class Node {
public int no;
public Node pre; //定义前置指针用来逆序输出
public Node next;
// 构造函数
public Node(int No) {
this.no = No;
}
// 重写toString,显示该方法
@Override
public String toString() {
return "Node [no=" + no + "]";
}
}
运行结果如下: