数据结构与算法之美——栈复习
1、课程内容
详情可参考“极客时间”上的《数据结构与算法之美》课程:08 | 栈:如何实现浏览器的前进和后退功能? (geekbang.org)
2、课后练习

代码
数组栈
package dataStruct;
/**
* @author: wu linchun
* @time: 2022/2/6 11:42
* @description: 数组栈
*/
public class ArrayStack {
//数组
public Object[] items;
//栈中元素个数
public int count;
//栈的大小
public int size;
public ArrayStack(int size) {
this.size = size;
this.items = new Object[size];
this.count = 0;
}
//入栈
public boolean push(Object item) {
//数组空间不够了,直接返回false,入栈失败
if (count == size) {
return false;
}
if (item != null) {
items[count] = item;
count++;
return true;
}
return false;
}
//出栈操作
public Object pop() {
if (count == 0) {
return null;
}
Object object = items[count - 1];
count--;
return object;
}
//获取栈顶元素
public Object getTop() {
Object obj = pop();
this.push(obj);
return obj;
}
}
链栈
package dataStruct;
/**
* @author: wu linchun
* @time: 2022/2/6 11:52
* @description: 链栈
*/
public class ChainStack {
//栈顶结点
public Node<Object> top;
//链表
public ListNode<Object> listNode;
//栈的大小
public int size;
//构造一个链栈
public ChainStack() {
//new一个链表
listNode = new ListNode<>();
}
//入栈
public boolean push(Object item) {
if (item != null) {
Node<Object> node = new Node(item);
listNode.addNode(node);
this.top = node;
size++;
return true;
}
return false;
}
//出栈
public Object pop() {
if (size > 0) {
Object obj = listNode.getLastNode().data;
listNode.removeLastN(1);
size--;
return obj;
}
return null;
}
//获取栈顶元素
public Object getTop() {
Object obj = pop();
this.push(obj);
return obj;
}
}
注:链表栈的数据结构中用到的Node和ListNode相关代码请参考:数据结构与算法之美——单链表复习_金斗潼关的博客-CSDN博客
模拟浏览器前进后退
package dataStruct;
import java.util.Scanner;
/**
* @author: wu linchun
* @time: 2022/2/6 14:13
* @description: 模拟浏览器前进后退
* 0表示结束输入,f表示前进,b表示后退,输入其他字符为网址输入
*/
public class BrowserFBImitate {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ChainStack chainStackX = new ChainStack();
ChainStack chainStackY = new ChainStack();
String website;
String cmd = input.nextLine();
while (!cmd.equals("0")) {
//cmd=f/b表示网页前进后退
if (!(cmd.equals("f") || cmd.equals("b"))) {
website = cmd;
chainStackX.push(website);
}
//f表示网页前进
if (cmd.equals("f")) {
Object obj = chainStackY.pop();
if (obj != null) {
System.out.println(chainStackY.getTop() != null ? chainStackY.getTop() : obj);
chainStackX.push(obj);
} else {
System.out.println(chainStackX.getTop() + "---已经是最前的网页了");
}
} else if (cmd.equals("b")) {
Object obj = chainStackX.pop();
if (obj != null) {
System.out.println(chainStackX.getTop() != null ? chainStackX.getTop() : obj);
chainStackY.push(obj);
} else {
System.out.println(chainStackY.getTop() + "---已经是最后的网页了");
}
}
cmd = input.nextLine();
}
}
}

3、总结
栈的很简单,用处也很大。栈的使用场景有:函数调用(通过栈实现递归),浏览器浏览网页前进后退功能(用两个栈存放访问,一个负责后退,一个负责前进)。
摘录两个课后思考的问题:
Q1、我们在讲栈的应用时,讲到用函数调用栈来保存临时变量,为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?
A1、其实,我们不一定非要用栈来保存临时变量,只不过如果这个函数调用符合后进先出的特性,用栈这种数据结构来实现,是最顺理成章的选择。 从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。所以根本上,只要能保证每进入一个新的函数,都是一个新的作用域就可以。而要实现这个,用栈就非常方便。在进入被调用函数的时候,分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。 ------引自评论区。 这里解释一下,最重要的是因为只能操作"栈顶元素",在作用域的角度看,操作完一个作用域的再回到之前的作用域下,用栈保存临时变量则是最好的选择,其他的数组,链表等都能"违规地"进行随机访问。

函数调用是一个递归调用子函数的过程,而实现递归的最简单的方法就是用栈!!!
Q2、我们都知道,JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用,堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟我们这里说的“栈”是不是一回事呢?如果不是,那它为什么又叫作“栈”呢?
A2、内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。它们都具备栈的“先进后出”特性! 内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区。 代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。 静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。 栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。 堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

浙公网安备 33010602011771号