LeetCode题解——栈、队列

这篇博客介绍了使用栈解决LeetCode中的几道经典问题,包括有效括号、最小栈、柱形图中的最大矩形和滑动窗口最大值。解题方法涉及栈的基本操作和巧妙应用,如动态规划和暴力法的比较,以及如何优化算法效率。
摘要由CSDN通过智能技术生成

如有问题,敬请指正!

20.有效的括号

1. 解法一:使用栈进行判断

class Solution {
    public boolean isValid(String s) {
        if (s == null || s.length() == 0) return true;
        char[] arr = s.toCharArray();
        // 将arr中的元素放到栈中去判断
        Stack<Character> stack = new Stack<>();
        for (int i = 0;i < arr.length; i ++) { 
            // 如果是左括号就入栈
            if (arr[i] == '[' || arr[i] == '{' || arr[i] == '(') {
                stack.push(arr[i]);
                // 否则,将这个元素和栈顶元素匹配
                // 如果匹配成功,将栈顶元素出栈
                // 否则,返回 false
            } else {
                // 确保第奇数个括号为右括号时,栈不为空
                if (arr[i] == ']' && !stack.empty() && stack.peek() == '[') {
                    stack.pop();
                } else if (arr[i] == '}' && !stack.empty() && stack.peek() == '{') {
                    stack.pop();
                } else if (arr[i] == ')' && !stack.empty() && stack.peek() == '(') {
                    stack.pop();
                } else {
                    return false;
                }
            }
        }
        // 如果栈不为空,说明栈中还没有被匹配从而弹出的元素
//       if (stack.empty()) {
//            return true;
//        } else {
//            return false;
//        }
        return stack.empty();
    }
}
  1. 这一道题在条件判断时出现很多问题,首先要确保的就是在第奇数个位置的元素不能为右括号,那么在第16-20行就要每次都判断栈不能为空,如果栈为空,后续的stack.peek() 操作就是一个非法操作
  2. 左括号时入栈,右括号时将这个右括号和栈顶元素进行匹配,首先需要保证栈不为空,如果这个时候拿到一个右括号但栈为空,说明匹配不上,要么是这个数组元素有奇数个,要么这个右括号出现在奇数位置

2.解法二:还是用栈,更简洁

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            // 遇到左括号,下次希望出现右括号和他配对,所以把对应的右括号加入栈中
            // 下次在10行时,弹出的右括号是存进去的右括号,说明配对成功
            if (c == '(') stack.push(')');
            else if (c == '[') stack.push(']');
            else if (c == '{') stack.push('}');
            else if (stack.empty() || stack.pop() != c) return false;
        }       
        return stack.empty();
    }
}

155.最小栈

1.解法一

class MinStack {
    private Stack<Integer> stackData;   // 记录最小栈中的数据
    private Stack<Integer> stackMin;    // 记录最小值的栈
    /** initialize your data structure here. */
    public MinStack() {
        stackData = new Stack<>();
        stackMin = new Stack<>();
    }
    
    public void push(int x) {
        // 如果最小值栈为空,直接压栈
        if (stackMin.empty()) {
            stackMin.push(x);
            // 这个地方要千万注意!!!!!!!!!
            // 因为最小值可能有多个,所以要把等于的纳入考虑
        } else if (x <= this.getMin()) {
            stackMin.push(x);
        }
        // 不管如何都要入栈
        stackData.push(x);
    }
    
    // 出栈时需要维护最小值栈
    public void pop() {
        if (stackData.empty()) {
            throw new RuntimeException("Your stack is empty!");
        }
        // 如果出栈元素为当前最小值,将最小值栈栈顶元素弹出
        if (stackData.pop() == this.getMin()) stackMin.pop();
    }
    
    public int top() {
        if (stackData.empty()) {
            throw new RuntimeException("Your stack is empty!");
        }
        return stackData.peek(); 
    }
    
    public int getMin() {
        if (stackMin.empty()) {
            throw new RuntimeException("Your stack is empty!");
        }
        return stackMin.peek();
    }
}

84.柱形图中的最大矩形

1·解法一:暴力法 O(n 2 ^2 2)

class Solution {
    public int largestRectangleArea(int[] heights) {
        int maxarea = 0;       
        // 柱子面积可以就是一根柱子的面积,所以i可以到达最后一个位置
        for (int i = 0;i < heights.length;i ++) {
            // 每一轮都要记录一个新的最小高度
            int minHeight = Integer.MAX_VALUE;
            for (int j = i;j < heights.length;j ++) {
                minHeight = Math.min(minHeight, heights[j]);
                maxarea = Math.max(maxarea, minHeight * (j - i + 1));
            }
        }
        return maxarea;
    }
}

2.看题解去:使用栈

class Solution {
    public int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        int maxArea = 0;
        for (int i = 0;i < heights.length;i ++) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) {
                maxArea = Math.max(maxArea, heights[stack.pop()] * (i - stack.peek() - 1));
            }
            stack.push(i);
        }
        while (stack.peek() != -1) {
            maxArea = Math.max(maxArea, heights[stack.pop()] * (heights.length - stack.peek() - 1));
        }
        return maxArea;
    }
}

239.滑动窗口最大值

1.解法一

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || k <= 0) return new int[0];
        int n = nums.length;
        int[] res = new int[n - k + 1];
        int size = 0; // record size of res
        Deque<Integer> q = new ArrayDeque<>();
        for (int i = 0;i < n;i ++) {
            // remove numbers out of range k
            if (!q.isEmpty() && q.peek() < i - k + 1) {
                q.poll();
            }
            // remove smaller numbers in k range as they are useless
            while (!q.isEmpty() && nums[q.peekLast()] < nums[i]) {
                q.pollLast();
            }
            // q contains index
            // res contains result
            q.offer(i);
            // only when i >= k - 1, there are enough nums to add in res
            if (i >= k - 1) {
                res[size ++] = nums[q.peek()];
            }
        }
        return res;
    }
}

42.接雨水

1.解法一:暴力法

1. 直观想法:

直接按问题描述来说。对于数组中的每一个元素,我们找出下雨后能达到的最高位置,等于两边最大高度的较小值减去当前高度的值。

在这里插入图片描述

2.算法
  1. 初始化返回值 ans
  2. 从第一个元素开始向右扫描整个数组
    1. 初始化max_left = 0max_right = 0
    2. 从当前元素向左扫描并更新 max_left = max(max_left, height[j])
    3. 从当前元素向右扫描并更新 max_right = max(max_right, height[j])
    4. min(max_left, max_right) - height[j] 累加到结果 ans
  3. 时间复杂度 O(n 2 ^2 2)
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int L = height.length;
        for (int i = 1;i < L;i ++) {
            int max_left = 0, max_right = 0;
            // scan from this to left
            for (int j = i;j >= 0;j --) {
                max_left = Math.max(max_left, height[j]);
            }
            // scan from this to right
            for (int j = i;j < L;j ++) {
                max_right = Math.max(max_right, height[j]);
            }
            ans += Math.min(max_left, max_right) - height[i];
        }
        return ans;
    }
}

2.解法二:动态编程

1.直观想法:

在暴力解决中,我们对每一个元素都向左向右遍历一次,导致时间复杂度为 O(n 2 ^2 2) ,我们可以提前存储下来这个值。先从左遍历一遍整个数组,记录下每一个元素左边的最大值;再从右遍历一遍整个数组,记录下每一个元素右边的最大值;最后再遍历一遍整个数组,利用和暴力法一样的手段,求得最终结果。

2.算法
  1. 找到数组中从下标 i 到最左端最高的条形块高度 left_max
  2. 找到数组中从下标 i 到最右端最高的条形块高度 right_max
  3. 扫描 height 并更新答案
    1. 累加min(max_left[i], max_right[i]) - height[i]res
  4. 时间复杂度 O(n)
class Solution {
    public int trap(int[] height) {
        if (height == null || height.length <= 1) return 0;
        int res = 0;
        int L = height.length;
        int[] max_left = new int[L];
        int[] max_right = new int[L];
        // initialize max_left[0] with height[0],因为我们遍历是从1开始的
        max_left[0] = height[0];
        for (int i = 1;i < L;i ++) {
            // compare with left one
            max_left[i] = Math.max(height[i], max_left[i - 1]);
        }
        // because the num in L - 1 has no more nums in right
        // initialize max_right[L - 1] with height[L - 1],因为我们遍历是从L-2开始的
        max_right[L - 1] = height[L - 1];
        for (int i = L - 2;i >= 0;i --) {
            // compare with right one
            max_right[i] = Math.max(height[i], max_right[i + 1]);
        }
        for (int i = 0;i < L;i ++) {
            res += Math.min(max_left[i], max_right[i]) - height[i];
        }
        return res;
    }
}

3.解法三:使用栈

我们在遍历数组时维护一个栈。如果当前的条形块小于或等于栈顶的条形快,我们将条形快的索引入栈,意思是当前的条形快被栈中的前一个条形快所界定。如果我们发现一个条形快长于栈顶,我们可以确定栈顶的条形快被当前条形块和栈顶的前一个条形块界定,因此我们可以弹出栈顶元素并且累加答案到 res

1.算法
  1. 使用栈来存储当前条形块的索引下标

  2. 遍历数组

    1. 当栈非空且当前条形块height[curr] 高于栈顶条形块height[stack.peek()]

      1. 栈中元素可以被弹出,弹出栈顶元素 top

      2. 如果此时栈为空,break 循环

      3. 否则,计算当前元素和栈顶元素的距离 distance = curr - stack.peek() - 1

      4. 找出界定高度:弹出的栈顶元素的左右两边的最小值减去弹出的栈顶元素值

        bounded_height = Math.min(height[curr], height[stack.peek()]) - height[top]

      5. 累加答案 res += distance * bounded_height

    2. 将当前下标索引入栈,并后移 curr

class Solution {
    public int trap(int[] height) {
        int res = 0, curr = 0;
        Stack<Integer> stack = new Stack<>();
        while (curr < height.length) {
            while (!stack.empty() && height[curr] > height[stack.peek()]) {
                int top = stack.pop();
                if (stack.empty()) break;
                int distance = curr - stack.peek() - 1;
                int bounded_height = Math.min(height[curr], height[stack.peek()]) - height[top];
                res += distance * bounded_height;
            }
            stack.push(curr ++);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值