栈(Stack)是一种重要的数据结构,它遵循“先进后出”(LIFO, Last In, First Out)的原则。栈是一种抽象数据类型,意味着它定义了数据的操作而不具体指定如何实现。栈可以通过不同的数据结构来实现,例如数组或链表
1. 基本概念
LIFO(Last In, First Out):栈遵循先进后出原则,即最后添加的元素最先被移除。例如,想象一个堆叠的盘子,最后放上去的盘子会最先被取走。
操作:
入栈(Push):将元素添加到栈的顶端。
出栈(Pop):从栈的顶端移除并返回元素。
查看栈顶元素(Peek/Top):查看栈顶的元素但不移除它。
检查栈是否为空(isEmpty):判断栈是否为空,即没有元素。
2. 栈的应用
函数调用管理:程序在调用函数时,会将函数的执行状态保存到栈中,这样函数执行完毕后,可以恢复到调用点继续执行。这是栈在编程中的一个重要应用。
撤销操作:在文本编辑器或图形软件中,撤销操作通常使用栈来管理用户的操作历史。
表达式求值:栈可以用来计算后缀表达式(逆波兰表示法)和处理中缀表达式的运算符优先级。
回溯算法:在解决复杂问题时,栈用于回溯算法来记录每一步的状态,以便可以在遇到问题时回到先前的状态。
3. 栈的实现
栈可以通过不同的数据结构实现,常见的实现方法有:
数组实现:
优点:操作简单,访问元素速度快。
缺点:固定大小,可能浪费内存或需要重新分配空间。
链表实现:
优点:动态大小,可以有效利用内存。
缺点:相较于数组,访问元素速度稍慢,需要额外的空间来存储指针。基于数组的实现栈
package com.rojer.pri;
import java.util.EmptyStackException;
/**
* 基于数组实现的栈
*/
public class StackUsingArray {
// 实际存储数组
private final Object[] stack;
// 栈帧
private int top;
// size
private final int capacity;
// 构造函数定长
public StackUsingArray(int size) {
capacity = size;
stack = new Object[capacity];
top = -1;
}
// 添加元素到栈帧
public void push(int value) {
if (top == capacity - 1) {
throw new StackOverflowError("Stack is full");
}
stack[++top] = value;
}
// 取出栈帧
public Object pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack[top--];
}
// 获取栈帧
public Object peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack[top];
}
// 判断空
public boolean isEmpty() {
return top == -1;
}
public static void main(String[] args) {
StackUsingArray stack = new StackUsingArray(5);
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Top element: " + stack.peek()); // Output: 30
System.out.println("Popped element: " + stack.pop()); // Output: 30
System.out.println("Top element after pop: " + stack.peek()); // Output: 20
StackUsingArray stack1 = new StackUsingArray(3);
stack1.push(11);
stack1.push(22);
stack1.push(33);
System.out.println("Top element: " + stack1.peek()); // Output: 30
System.out.println("Popped element: " + stack1.pop()); // Output: 30
System.out.println("Top element after pop: " + stack1.peek()); // Output: 20
}
}
基于链表实现的栈
package com.rojer.pri;
import java.util.EmptyStackException;
import java.util.LinkedList;
public class StackUsingLinkedList {
private LinkedList<Integer> stack = new LinkedList<>();
public void push(int value) {
stack.addFirst(value);
}
public int pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack.removeFirst();
}
public int peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack.getFirst();
}
public boolean isEmpty() {
return stack.isEmpty();
}
public static void main(String[] args) {
StackUsingLinkedList stack = new StackUsingLinkedList();
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Top element: " + stack.peek()); // Output: 30
System.out.println("Popped element: " + stack.pop()); // Output: 30
System.out.println("Top element after pop: " + stack.peek()); // Output: 20
}
}
4. 优缺点
优点:
简单性:栈是一种简单而直观的数据结构,易于实现和理解。
高效:栈的基本操作(入栈、出栈、查看栈顶)都是常数时间复杂度 O(1)。
灵活性:栈可以用在各种算法和数据处理中,如深度优先搜索、递归等。
缺点:
单向访问:栈只允许从一端进行操作,不支持随机访问或在中间进行操作。
内存限制:在使用数组实现时,栈的容量是固定的,可能导致栈溢出或内存浪费。
5,常用jdk的栈的实现类
Stack
- java.util.Stack 是一个直接实现了栈的类,它继承自 Vector 类并实现了 List 接口。这个类提供了常见的栈操作,如 push()、pop()、peek()、empty() 和 search()。
- 由于 Stack 继承自 Vector,它是同步的,但它不建议在高并发环境下使用,效率较低。
ArrayDeque
- ArrayDeque 是 Deque 接口的一个实现,通常用来实现栈和队列。它提供了无界的双端队列,并且比 Stack 类更高效。
- 使用 ArrayDeque 实现栈可以通过 push()、pop() 和 peek() 方法来完成。
LinkedList
- LinkedList 也是 Deque 接口的一个实现,同样可以用来实现栈和队列。它基于链表结构,适用于需要频繁插入和删除元素的场景。
- 使用 LinkedList 实现栈同样可以通过 push()、pop() 和 peek() 方法来完成。
6,leetcode中相关题目
简单难度
20,155,232,255,1021,1047
中等难度
739,496,150,503,946
困难
32