什么是栈/Java中的栈(栈定义/栈的时间复杂度/实现一个栈/栈的动态扩容)含示例代码

入门

如何理解“栈”

  • 他是一种受限的线性表
  • 分为 顺序栈/链式栈
  • 是一种 后进先出 的数据结构

如何实现一个“栈”

  • 栈主要包含两个操作,出栈和入栈。
  • 可以使用 链表或数据 存储数据
  • 分类:顺序栈、链式栈

支持动态扩容的顺序栈

image.png

image.png

  • 顺序栈可以支持动态扩容
  • 当容量达到上限,进行一次数据迁移即可。
  • 复杂度分析
    • 最好时间复杂度:O(1)
      • 简单的 push 操作。
    • 最坏时间复杂度:O(n)
      • 涉及到数据迁移,需要将原有 栈 中的数据,迁移到一个新的 栈 中。
      • 平均时间复杂度(摊还分析法):O(1)
    • 只有在容量达到 capacity 的时候才会进行数据迁移,此次的时间复杂度为 O(n)。
      • 将此次的时间复杂度,均摊到之前的 N-1 次,则每次的操作都只是均摊 O(1)

 

栈在软件工程中的实际应用

  • 浏览器的前进后退功能
    • 使用两个栈,一个存放前进,一个存放后退
  • JVM 栈的实现
    • 每个方法的调用都会生成一个栈帧,变量会被 push 都栈帧

image.png

  • 实现简单的计算器
    • 一个操作数栈、一个操作符栈。
    • 如果待入栈操作符的优先级等于或小于栈顶优先级,则进行计算

image.png

  • 检测括号(大中小括号)是否匹配
    • 左括号入栈,右括号与栈顶括号对比,如果一直则出栈

 

思考

为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?

  • 从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。
  • 所以根本上,只要能保证每进入一个新的函数,都是一个新的作用域就可以。而要实现这个,用栈就非常方便。
  • 在进入被调用函数的时候,分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。

 

实现

顺序栈

public class ArrayStack {
    private final String[] items;
    private final int capacity;
    private int index = 0;

    public ArrayStack(int capacity) {
        this.capacity = capacity;
        items = new String[capacity];
    }

    public boolean push(String item) {
        if (index == capacity) return false;
        items[index++] = item;
        return true;
    }

    public String pop() {
        if (index == 0) return null;
        return items[--index];
    }

    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(10);
        System.out.println("first pop:" + stack.pop());
        Assert.isTrue(stack.push("123"));
        Assert.equals("123", stack.pop());
        System.out.println(stack.pop());
    }
}

链式栈

public class LinkedStack {

    //头结点是哨兵节点,不需要额外处理 null
    private ListNode current = new ListNode(null);

    public boolean push(String item) {
        ListNode next = new ListNode(item);
        //1. 给 cur 的next 赋值
        this.current.next = next;
        //2. 给 next 的 prev 赋值
        next.prev = current;
        //3. 当前指针指向下一个
        current = next;
        return true;
    }

    public String pop() {
        if (this.current.prev == null) {
            return null;
        }
        String val = current.val;
        current = current.prev;
        return val;
    }


    public static void main(String[] args) {
        LinkedStack stack = new LinkedStack();
        System.out.println("first pop:" + stack.pop());
        Assert.isTrue(stack.push("123"));
        Assert.equals("123", stack.pop());
        System.out.println("end:" + stack.pop());
        System.out.println("end:" + stack.pop());
    }
}

class ListNode {
    String val;
    ListNode next;
    ListNode prev;

    ListNode(String x) {
        val = x;
    }
}

LeetCode 拓展

 

20 155 232 844 224 682 496.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值