数据结构与算法之美——栈复习

1、课程内容

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

2、课后练习

数据结构与算法之美——栈复习_第1张图片

 代码

数组栈

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 top;
    //链表
    public ListNode listNode;
    //栈的大小
    public int size;

    //构造一个链栈
    public ChainStack() {
        //new一个链表
        listNode = new ListNode<>();
    }

    //入栈
    public boolean push(Object item) {
        if (item != null) {
            Node 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();
        }
    }
}

数据结构与算法之美——栈复习_第2张图片

 

3、总结

栈的很简单,用处也很大。栈的使用场景有:函数调用(通过栈实现递归),浏览器浏览网页前进后退功能(用两个栈存放访问,一个负责后退,一个负责前进)。

摘录两个课后思考的问题:

Q1、我们在讲栈的应用时,讲到用函数调用栈来保存临时变量,为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?

A1、其实,我们不一定非要用栈来保存临时变量,只不过如果这个函数调用符合后进先出的特性,用栈这种数据结构来实现,是最顺理成章的选择。 从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。所以根本上,只要能保证每进入一个新的函数,都是一个新的作用域就可以。而要实现这个,用栈就非常方便。在进入被调用函数的时候,分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。 ------引自评论区。 这里解释一下,最重要的是因为只能操作"栈顶元素",在作用域的角度看,操作完一个作用域的再回到之前的作用域下,用栈保存临时变量则是最好的选择,其他的数组,链表等都能"违规地"进行随机访问。

 函数调用是一个递归调用子函数的过程,而实现递归的最简单的方法就是用栈!!!

Q2、我们都知道,JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用,堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟我们这里说的“栈”是不是一回事呢?如果不是,那它为什么又叫作“栈”呢?

A2、内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。它们都具备栈的“先进后出”特性! 内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区。 代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。 静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。 栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。 堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

4、参考资料

  • 08 | 栈:如何实现浏览器的前进和后退功能? (geekbang.org)
  • 数据结构与算法之美——单链表复习_金斗潼关的博客-CSDN博客
  • 作用域_百度百科 (baidu.com)

你可能感兴趣的:(数据结构与算法之美,数据结构,算法,栈)