栈(Stack)是一种后进先出(LIFO,Last In First Out)数据结构,意味着最后进栈的元素最先被弹出。栈是一种非常简单但功能强大的数据结构,广泛应用于程序的函数调用栈、表达式计算、深度优先搜索(DFS)等场景。
1. 栈的基本操作
1.1 栈有三个基本操作:
-
压栈(Push):将元素压入栈顶。
-
弹栈(Pop):从栈顶弹出元素。
-
查看栈顶元素(Peek):返回栈顶元素,但不移除它。
1.2 LIFO vs FIFO
为了理解栈的工作原理,我们首先回顾一下队列(Queue)的特点。队列遵循先进先出(FIFO,First In First Out)原则,即最先进入队列的元素最早出队列。而栈遵循后进先出(LIFO)原则,即最后进入栈的元素最早弹出。
以下是一个简化的视觉对比:
1.2.1 队列(FIFO):
less入队列: A -> B -> C -> D出队列: A <- B <- C <- D
1.2.2 栈(LIFO):
less入栈: A -> B -> C -> D出栈: D <- C <- B <- A
1.3 Java 中的栈实现
在 Java 中,可以使用 Deque 接口来模拟栈的功能。Deque(双端队列)是一个可以在两端进行操作的数据结构,我们可以利用它来实现栈的操作。虽然 Java 提供了一个遗留类 Stack 来表示栈,但不推荐使用它,因其过时且功能不如 Deque 灵活。
2. Deque 实现栈操作
Deque 接口提供了多种方法来进行栈的操作,我们可以利用以下方法来实现栈的功能:
-
压栈:
addFirst(E e)或offerFirst(E e) -
弹栈:
removeFirst()或pollFirst() -
查看栈顶元素:
getFirst()或peekFirst()
示例代码:使用 Deque 实现栈
javaimport java.util.Deque;import java.util.LinkedList;public class StackDemo {public static void main(String[] args) {Deque<String> stack = new LinkedList<>();// 压栈stack.addFirst("A");stack.addFirst("B");stack.addFirst("C");// 查看栈顶元素System.out.println("栈顶元素: " + stack.peekFirst()); // 输出: C// 弹栈System.out.println("弹出栈顶元素: " + stack.removeFirst()); // 输出: CSystem.out.println("弹出栈顶元素: " + stack.removeFirst()); // 输出: B// 查看栈顶元素System.out.println("栈顶元素: " + stack.peekFirst()); // 输出: A}}
在这个例子中,使用 LinkedList 来实现 Deque 接口,并通过 addFirst() 方法压栈,removeFirst() 方法弹栈,peekFirst() 方法查看栈顶元素。
3. 为什么不推荐使用遗留类 Stack
尽管 Java 早期提供了 Stack 类,它继承自 Vector,并且具有 push(), pop(), 和 peek() 等方法,但 Stack 类并没有现代 Deque 接口的多功能性,且其性能和灵活性较差。因此,推荐使用 Deque 来实现栈的功能。
4. 栈的应用
栈在计算机科学中有广泛的应用。以下是一些常见的使用场景:
4.1 方法调用栈
当一个程序执行时,每当一个方法被调用时,JVM 会将该方法的执行状态压入栈中。当方法执行完毕后,它会从栈中弹出,返回调用者。栈的这种行为使得程序能够正确地进行方法的调用与返回。
例如,在执行以下递归函数时,JVM 会使用栈来维护每个递归调用的状态:
javapublic class Main {public static void main(String[] args) {increase(1);}static int increase(int x) {return increase(x) + 1; // 无限递归}}
如果递归调用太深,会导致栈溢出,抛出 StackOverflowError 错误。
4.2 十进制转十六进制
栈在进制转换中的应用也非常常见。举个例子,我们可以用栈来将一个整数转换为十六进制。以下是具体步骤:
-
将数字除以 16,获取余数,并将余数压栈。
-
重复上述操作,直到商为 0。
-
从栈中弹出所有元素,形成最终的十六进制字符串。
javaimport java.util.*;public class StackHexConversion {public static void main(String[] args) {int number = 12500;String hex = toHex(number);System.out.println("十六进制表示: " + hex); // 输出: 30D4}static String toHex(int n) {if (n == 0) return "0";Deque<Character> stack = new LinkedList<>();String hexChars = "0123456789ABCDEF";while (n > 0) {stack.push(hexChars.charAt(n % 16));n /= 16;}StringBuilder result = new StringBuilder();while (!stack.isEmpty()) {result.append(stack.pop());}return result.toString();}}
4.3 计算后缀表达式
后缀表达式(逆波兰表示法)是计算表达式的一种方式,可以通过栈来计算。下面是计算后缀表达式 1 2 9 5 - * + 的示例:
javaimport java.util.*;public class SuffixExpressionCalculator {public static void main(String[] args) {String expression = "1 2 9 5 - * +";System.out.println("结果: " + calculateSuffixExpression(expression)); // 输出: 9}static int calculateSuffixExpression(String exp) {Deque<Integer> stack = new LinkedList<>();String[] tokens = exp.split(" ");for (String token : tokens) {if (isOperator(token)) {int b = stack.pop();int a = stack.pop();int result = applyOperator(a, b, token);stack.push(result);} else {stack.push(Integer.parseInt(token));}}return stack.pop();}static boolean isOperator(String token) {return token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/");}static int applyOperator(int a, int b, String operator) {switch (operator) {case "+": return a + b;case "-": return a - b;case "*": return a * b;case "/": return a / b;default: throw new IllegalArgumentException("Unknown operator " + operator);}}}
5. 小结
-
栈(Stack)是一种后进先出(LIFO)的数据结构,主要操作有压栈(push)、弹栈(pop)和查看栈顶元素(peek)。
-
在 Java 中,可以使用
Deque接口来实现栈,Deque提供了与栈操作相对应的方法:addFirst()(压栈)、removeFirst()(弹栈)、peekFirst()(查看栈顶元素)。 -
不建议使用过时的
Stack类,推荐使用Deque实现栈功能。 -
栈在方法调用管理、进制转换、后缀表达式计算等多种场景中都有广泛应用。
通过这些例子,你可以更好地理解栈的实现和应用,并在实际开发中灵活使用它。
4283

被折叠的 条评论
为什么被折叠?



