搞定leetcode面试经典150题之栈

系列博客目录



理论知识

Java 中的栈(Stack)是一种特殊的线性数据结构,它遵循后进先出(LIFO, Last In First Out)的原则。栈的基本操作包括入栈(push)、出栈(pop)和查看栈顶元素(peek)。下面是一些栈的基础知识:

1. 栈的基本概念

栈是一种仅允许从一端进行插入和删除操作的数据结构。插入和删除操作都发生在栈顶元素,常见的比喻是“餐盘堆叠”,最后放入的盘子是第一个被取走的。

2. 栈的主要操作

  • push:将元素压入栈顶。
  • pop:移除栈顶元素并返回它。
  • peek(或 top):返回栈顶元素,但不移除它。
  • isEmpty:检查栈是否为空。
  • size:返回栈中元素的数量。

3. 栈的实现

Java 并没有直接提供栈的接口,但可以使用 java.util.Stack 类实现栈操作。Stack 类是 Vector 类的一个子类,它实现了栈的常见操作。

示例代码:

import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        
        // 入栈操作
        stack.push(10);
        stack.push(20);
        stack.push(30);
        
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());  // 30
        
        // 出栈操作
        System.out.println("出栈元素: " + stack.pop());  // 30
        
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());  // 20
        
        // 检查栈是否为空
        System.out.println("栈是否为空: " + stack.isEmpty());  // false
    }
}

4. 栈的应用

栈在程序设计中有很多实际应用:

  • 递归调用:递归调用本质上依赖于栈来保存调用函数的上下文。
  • 表达式求值:例如,在中缀表达式转后缀表达式时,栈用于临时存储操作符。
  • 回溯算法:如深度优先搜索(DFS)中,栈用于保存节点路径。

5. 栈的性能

栈的操作(pushpoppeek)时间复杂度为 O(1),因为它们只是对栈顶元素进行操作,因此栈的性能是非常高效的。

6. 注意事项

  • Stack 类是同步的,但其性能可能比其他非同步数据结构(如 LinkedList)差。因此,若不需要同步操作,建议使用 LinkedListArrayDeque 来实现栈。
  • 现代 Java 中,Stack 类通常被认为是过时的,推荐使用 Deque(双端队列)接口的实现类,如 ArrayDeque,来实现栈。

示例代码:使用 ArrayDeque 实现栈

import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeStackExample {
    public static void main(String[] args) {
        Deque<Integer> stack = new ArrayDeque<>();
        
        // 入栈操作
        stack.push(10);
        stack.push(20);
        stack.push(30);
        
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());  // 30
        
        // 出栈操作
        System.out.println("出栈元素: " + stack.pop());  // 30
        
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());  // 20
    }
}

ArrayDeque 是 Java 中一个非常常用的类,提供了一个基于数组实现的双端队列(Deque)。ArrayDeque 允许在队列的两端进行插入、删除和访问元素,因此它可以用来实现栈(LIFO)和队列(FIFO)等数据结构。由于它是基于动态数组实现的,因此在效率上比 LinkedList 更好,尤其是在需要频繁操作队列头尾时。

ArrayDeque 类概述

ArrayDequeDeque 接口的一个实现,继承自 AbstractCollection,提供了常见的队列和栈操作。ArrayDeque 适用于那些对性能要求较高的场景,尤其是在不需要线程安全的情况下。

主要方法

以下是 ArrayDeque 中常见的各种方法及其用途:

1. add(E e) / offer(E e)
  • add(E e):将元素添加到队列的尾部。如果队列的容量满了,它会抛出 IllegalStateException

  • offer(E e):与 add(E e) 类似,但它不会抛出异常。如果队列满了,它返回 false,而不是抛出异常。通常用于容量受限的队列。

    ArrayDeque<Integer> deque = new ArrayDeque<>();
    deque.add(10);    // 将 10 添加到队列尾部
    deque.offer(20);  // 将 20 添加到队列尾部
    
2. addFirst(E e) / offerFirst(E e)
  • addFirst(E e):将元素添加到队列的头部。如果队列的容量满了,它会抛出 IllegalStateException

  • offerFirst(E e):与 addFirst(E e) 类似,但它不会抛出异常。如果队列满了,它返回 false

    deque.addFirst(5);  // 将 5 添加到队列头部
    deque.offerFirst(2); // 将 2 添加到队列头部
    
3. remove() / poll()
  • remove():移除并返回队列的头部元素。如果队列为空,会抛出 NoSuchElementException

  • poll():与 remove() 相似,但如果队列为空,它返回 null

    deque.remove();    // 移除并返回队列头部的元素,如果队列为空会抛出异常
    deque.poll();      // 移除并返回队列头部的元素,如果队列为空会返回 null
    
4. removeFirst() / pollFirst()
  • removeFirst():移除并返回队列的头部元素。如果队列为空,会抛出 NoSuchElementException

  • pollFirst():与 removeFirst() 类似,但如果队列为空,它返回 null

    deque.removeFirst();   // 移除并返回队列头部的元素
    deque.pollFirst();     // 移除并返回队列头部的元素,如果队列为空返回 null
    
5. removeLast() / pollLast()
  • removeLast():移除并返回队列的尾部元素。如果队列为空,会抛出 NoSuchElementException

  • pollLast():与 removeLast() 类似,但如果队列为空,它返回 null

    deque.removeLast();   // 移除并返回队列尾部的元素
    deque.pollLast();     // 移除并返回队列尾部的元素,如果队列为空返回 null
    
6. getFirst() / peekFirst()
  • getFirst():返回队列的头部元素,但不移除它。如果队列为空,会抛出 NoSuchElementException

  • peekFirst():与 getFirst() 类似,但如果队列为空,它返回 null

    deque.getFirst();    // 返回队列头部的元素,不移除
    deque.peekFirst();   // 返回队列头部的元素,不移除,如果队列为空返回 null
    
7. getLast() /peekLast()
  • getLast():返回队列的尾部元素,但不移除它。如果队列为空,会抛出 NoSuchElementException

  • peekLast():与 getLast() 类似,但如果队列为空,它返回 null

    deque.getLast();     // 返回队列尾部的元素,不移除
    deque.peekLast();    // 返回队列尾部的元素,不移除,如果队列为空返回 null
    
8. push(E e)
  • push(E e):将元素压入双端队列的头部,相当于栈的“入栈”操作。push()addFirst()offerFirst() 的作用相似,都是将元素添加到队列头部。

    deque.push(10);  // 将 10 压入栈顶(队列头部)
    
9. pop()
  • pop():移除并返回队列的头部元素,类似栈的“出栈”操作。pop() 会抛出 NoSuchElementException 如果队列为空。

    deque.pop();  // 移除并返回队列头部的元素
    
10. clear()
  • clear():移除队列中的所有元素。

    deque.clear();  // 清空队列
    
11. size()
  • size():返回队列中的元素数量。

    int size = deque.size();  // 返回队列中元素的数量
    
12. isEmpty()
  • isEmpty():检查队列是否为空。如果队列为空,返回 true,否则返回 false

    boolean empty = deque.isEmpty();  // 判断队列是否为空
    
13. toArray()
  • toArray():返回队列中元素的一个数组副本。

    Object[] array = deque.toArray();  // 将队列元素转换为数组
    
14. contains(Object o)
  • contains(Object o):检查队列是否包含指定的元素。如果队列中存在该元素,返回 true,否则返回 false

    boolean contains = deque.contains(10);  // 判断队列中是否包含元素 10
    

总结

ArrayDeque 提供了一系列方法来操作双端队列的元素,支持从队列的两端进行插入、删除和查看元素。与其他 Deque 实现相比,ArrayDeque 在处理高效的队列和栈操作时具有明显优势。如果你只是把他当作栈来使用,那就用pollLast()peekLast()t和offerLast(),不要和push等连用!!!

例题

20.有效的括号

链接

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque<Character> stack = new LinkedList<Character>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/valid-parentheses/solutions/373578/you-xiao-de-gua-hao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

71.简化路径

链接

class Solution {
    public String simplifyPath(String path) {
        String[] names = path.split("/");
        Deque<String> stack = new ArrayDeque<String>();
        for (String name : names) {
            if ("..".equals(name)) {
                if (!stack.isEmpty()) {
                    stack.pollLast();
                }
            } else if (name.length() > 0 && !".".equals(name)) {
                stack.offerLast(name);
            }
        }
        StringBuffer ans = new StringBuffer();
        if (stack.isEmpty()) {
            ans.append('/');
        } else {
            while (!stack.isEmpty()) {
                ans.append('/');
                ans.append(stack.pollFirst());
            }
        }
        return ans.toString();
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/simplify-path/solutions/1193258/jian-hua-lu-jing-by-leetcode-solution-aucq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

150.求逆波兰表达式的值

链接

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new ArrayDeque<>();
        for(String str : tokens){
            if(isNumber(str)){
                stack.offerLast(Integer.valueOf(str));
            }else {
                int num2 = stack.pollLast();
                int num1 = stack.pollLast();
                switch (str) {
                    case "+":
                        stack.offerLast(num1 + num2);
                        break;
                    case "-":
                        stack.offerLast(num1 - num2);
                        break;
                    case "*":
                        stack.offerLast(num1 * num2);
                        break;
                    case "/":
                        stack.offerLast(num1 / num2);
                        break;
                    default:
                }
            }

        }
        return stack.pollLast();
    }
    private boolean isNumber(String str){
        return !(str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/"));
    }
}

155.最小栈

class MinStack {

    Deque<Integer> stack;
    Deque<Integer> minStack;
    public MinStack() {
        stack = new LinkedList<>();
        minStack = new LinkedList<>();
        minStack.push(Integer.MAX_VALUE);
    }

    public void push(int val) {
        stack.push(val);
        minStack.push(Math.min(minStack.peek(),val));
    }

    public void pop() {
        stack.pop();
        minStack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值