• 作者:老汪软件技巧
  • 发表时间:2024-10-30 21:03
  • 浏览量:

代码随想录之栈与队列篇T232-使用栈实现队列

见力扣第232题[使用栈实现队列]

题目描述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

我的思路

class MyQueue {
    private Stack pushStack;
    private Stack popStack;
    
    
    public MyQueue() {
        this.pushStack = new Stack<>();
        this.popStack = new Stack<>();
    }
    public void push(int x) {
        pushStack.push(x);
    }
    public int pop() {
        // 先检查 popStack 中是否有元素
        if (popStack.isEmpty()) {
            transfer(pushStack, popStack);
        }
        // 从 popStack 中弹出元素
        return popStack.pop();
    }
    public int peek() {
        if (popStack.isEmpty()) {
            transfer(pushStack, popStack);
        }
        return popStack.peek();
    }
    public boolean empty() {
        return popStack.isEmpty() && pushStack.isEmpty();
    }
    
    private void transfer(Stack pushStack, Stack popStack) {
        while (!pushStack.isEmpty()) {
            popStack.push(pushStack.pop());
        }
    }
}

T225-使用队列实现栈

见力扣第225题[使用队列实现栈]

题目描述

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

我的思路

class MyStack {
    private final ArrayList list;
    private int sz;
    public MyStack() {
        list = new ArrayList<>();
        sz = 0;
    }
    public void push(int x) {
        list.add(x);
        sz++;
    }
    public int pop() {
        int val = list.get(sz - 1);
        list.remove(sz - 1);
        sz--;
        return val;
    }
    public int top() {
        return list.get(sz - 1);
    }
    public boolean empty() {
        return sz == 0;
    }
}

T20-有效的括号

见力扣第20题[有效的括号]

题目描述

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

我的思路

判断stack是否为空

public boolean isValid(String s) {
    if (s.length() % 2 != 0) return false;
    Stack stack = new Stack<>();
    for (char c : s.toCharArray()) {
        if (!stack.isEmpty() && (c - stack.peek()) <= 2 && (c - stack.peek()) > 0) {
            stack.pop();
        } else {
            stack.push(c);
        }
    }
    return stack.isEmpty();
}

T1047-删除字符串中的所有相邻重复项

见力扣第1047题[删除字符串中的所有相邻重复项]

题目描述

给出由小写字母组成的字符串 s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 s 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"

我的思路

public String removeDuplicates(String s) {
    if (s.length() <= 1) return s;
    ArrayDeque q = new ArrayDeque<>();
    for (char c : s.toCharArray()) {
        if (!q.isEmpty() && c == q.peekLast()) {
            q.removeLast();
        } else {
            q.addLast(c);
        }
    }
    StringBuilder sb = new StringBuilder();
    for (char c : q) {
        sb.append(c);
    }
    return sb.toString();
}

队列实现栈java_栈和队列代码_

T150-逆波兰表达式求值

见力扣第150题[逆波兰表达式求值]

题目描述

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

我的思路

返回操作数栈中的运算结果

public int evalRPN(String[] tokens) {
    if (tokens.length <= 2) return Integer.parseInt(tokens[tokens.length - 1]);
    int[] dataStack = new int[tokens.length / 2 + 1]; // 最多 n / 2 + 1 个操作数
    int index = 0; // 模拟栈帧
    for (String token : tokens) {
        switch (token) {
            case "+": {
                // 加法,取出两位操作数相加,计算结果赋值到第一个操作数的位置
                dataStack[index - 2] = dataStack[index - 2] + dataStack[index - 1];
                // 栈帧指向有效操作数的后一位
                index--;
                break;
            }
            case "-": {
                dataStack[index - 2] = dataStack[index - 2] - dataStack[index - 1];
                index--;
                break;
            }
            case "*": {
                dataStack[index - 2] = dataStack[index - 2] * dataStack[index - 1];
                index--;
                break;
            }
            case "/": {
                dataStack[index - 2] = dataStack[index - 2] / dataStack[index - 1];
                index--;
                break;
            }
            default: {
                // 操作数,直接入栈
                dataStack[index++] = Integer.parseInt(token);
            }
        }
    }
    return dataStack[0];
}

时间复杂度:O(N)O(N)O(N),需要遍历一次tokens数组

空间复杂度:O(N)O(N)O(N),需要长度为(n/2)+1(n / 2) + 1(n/2)+1的数组模拟栈

T239-滑动窗口最大值

见力扣第239题[滑动窗口最大值]

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

我的思路

超时

思路

public int[] maxSlidingWindowI(int[] nums, int k) {
    int n = nums.length;
    Deque dq = new ArrayDeque<>();
    int[] res = new int[n - k + 1];
    int l = 0;
    int r = 0;
    while (r < k) {
        while (!dq.isEmpty() && nums[r] >= nums[dq.peekLast()]) {
            dq.pollLast();
        }
        dq.offerLast(r++);
    }
    res[l] = nums[dq.peekFirst()];
    while (r < n) {
        // 如果队尾索引指向的元素小于或者等于准备的入队的元素,直接弹出
        while (!dq.isEmpty() && nums[r] >= nums[dq.peekLast()]) {
            dq.pollLast();
        }
        dq.offerLast(r++);
        l++;
        // 如果队首索引不在窗口中
        while (dq.peekFirst() < l) {
            dq.pollFirst();
        }
        res[l] = nums[dq.peekFirst()];
    }
    return res;
}

T347-前K个高频元素

见力扣第347题[前K个高频元素]

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

我的思路

public int[] topKFrequent(int[] nums, int k) {
    Map map = new HashMap<>();
    for (int num : nums) {
        if (!map.containsKey(num)) {
            map.put(num, 0);
        }
        map.put(num, map.get(num) + 1);
    }
    List<int[]> frequency = new ArrayList<>();
    for (Map.Entry entry : map.entrySet()) {
        int[] pairs = new int[2];
        pairs[0] = entry.getKey();
        pairs[1] = entry.getValue();
        frequency.add(pairs);
    }
    Collections.sort(frequency, (o1, o2) -> o2[1] - o1[1]);
    int[] res = new int[k];
    for (int i = 0; i < k; i++) {
        res[i] = frequency.get(i)[0];
    }
    return res;
}

优化方案

public int[] topKFrequentI(int[] nums, int k) {
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    int[] ans = new int[k];
    // 找到数组中的最大值和最小值
    for (int i : nums) {
        max = Math.max(i, max); // 更新最大值
        min = Math.min(i, min); // 更新最小值
    }
    // 创建一个桶数组,用于记录每个数字出现的频率
    int[] buckets = new int[max - min + 1];
    for (int num : nums) {
        buckets[num - min]++; // 将数字映射到桶数组中,并增加频率
    }
    // 找到桶数组中的最大频率
    int maxF = 0;
    for (int bucket : buckets) {
        maxF = Math.max(bucket, maxF); // 更新最大频率
    }
    int index = 0; // 结果数组的索引
    // 找到出现频率最高的 k 个数字
    while (index < k) {
        for (int i = 0; i < buckets.length; i++) {
            if (maxF == buckets[i]) {
                ans[index] = i + min; // 将数字从桶数组映射回原数组的值
                index++;              // 更新结果数组的索引
            }
        }
        maxF--; // 减少最大频率,继续查找下一个频率最高的数字
    }
    return ans; // 返回结果数组
}

总结