入门
如何理解“栈”
- 他是一种受限的线性表
- 分为 顺序栈/链式栈
- 是一种 后进先出 的数据结构
如何实现一个“栈”
- 栈主要包含两个操作,出栈和入栈。
- 可以使用 链表或数据 存储数据
- 分类:顺序栈、链式栈
支持动态扩容的顺序栈
- 顺序栈可以支持动态扩容
- 当容量达到上限,进行一次数据迁移即可。
- 复杂度分析
- 最好时间复杂度:O(1)
- 简单的 push 操作。
- 最坏时间复杂度:O(n)
- 涉及到数据迁移,需要将原有 栈 中的数据,迁移到一个新的 栈 中。
- 平均时间复杂度(摊还分析法):O(1)
- 只有在容量达到 capacity 的时候才会进行数据迁移,此次的时间复杂度为 O(n)。
- 将此次的时间复杂度,均摊到之前的 N-1 次,则每次的操作都只是均摊 O(1)
- 最好时间复杂度:O(1)
栈在软件工程中的实际应用
- 浏览器的前进后退功能
- 使用两个栈,一个存放前进,一个存放后退
- JVM 栈的实现
- 每个方法的调用都会生成一个栈帧,变量会被 push 都栈帧
- 实现简单的计算器
- 一个操作数栈、一个操作符栈。
- 如果待入栈操作符的优先级等于或小于栈顶优先级,则进行计算
- 检测括号(大中小括号)是否匹配
- 左括号入栈,右括号与栈顶括号对比,如果一直则出栈
思考
为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?
- 从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。
- 所以根本上,只要能保证每进入一个新的函数,都是一个新的作用域就可以。而要实现这个,用栈就非常方便。
- 在进入被调用函数的时候,分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。
实现
顺序栈
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.