代码随想录-刷题第五十六天

单调栈理论基础

先介绍单调栈类型的题目,通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时就要想到可以用单调栈。时间复杂度为O(n)。

单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。

分析单调栈的时候,我们需要考虑以下几点。

① 单调栈里存放的元素是什么?

单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。

② 单调栈里元素是递增呢?还是递减呢?

这里要根据具体的题目来分析。

使用单调栈主要有三个判断条件。

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

把这三种情况分析清楚了,也就理解透彻了


739. 每日温度

题目链接:739. 每日温度

思路:借助单调栈来依次遍历数组,具体执行过程可以看代码随想录的分析。

自己再模拟一遍,就会发现:只有单调栈递增(从栈口到栈底顺序),就是求右边第一个比自己大的,单调栈递减的话,就是求右边第一个比自己小的。

java 复制代码
class Solution {
    public int[] dailyTemperatures(int[] temperatures) { 
        // 注意,单调栈里加入的元素是下标。
        int[] res = new int[temperatures.length];
        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);
        for (int i = 1; i < temperatures.length; i++) {
            if (temperatures[i] < temperatures[stack.peek()]) {
                // 情况一
                stack.push(i);
            } else if(temperatures[i] == temperatures[stack.peek()]){
                // 情况二
                stack.push(i);
            } else {
                // 情况三
                while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                    res[stack.peek()] = i - stack.peek();
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return res;
    }
}

496. 下一个更大元素 I

题目链接:496. 下一个更大元素 I

思路:本题相对于上一题增加了条件,难度也有所增加。

注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。可以用map做映射,根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。

遍历nums2,做单调栈处理,找到每一个元素右边最大元素,然后判断是否在nums1中存在,存在的话就要更新结果。

nums2的遍历过程与上一题相同。

java 复制代码
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] res = new int[nums1.length];
        // 题目说如果不存在对应位置就输出 -1
        Arrays.fill(res, -1);
        // map数组映射nums1,key对应值,val对应下标。
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums1.length; i++) {
            map.put(nums1[i], i);
        }

        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);

        for (int i = 1; i < nums2.length; i++) {
            if (nums2[i] < nums2[stack.peek()]) {
                // 情况一
                stack.push(i);
            } else if(nums2[i] == nums2[stack.peek()]){
                // 情况二
                stack.push(i);
            } else {
                // 情况三
                while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
                    if (map.containsKey(nums2[stack.peek()])) {
                        // 该数在nums1中存在,需要更新结果。
                        // 根据map找到nums2[stack.peek()]在nums1中的下标
                        int index = map.get(nums2[stack.peek()]);
                        res[index] = nums2[i];
                    }
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return res;
    }
}

503. 下一个更大元素II

题目链接:503. 下一个更大元素II

思路:本题仍然是利用单调栈来寻找下一个更大数值的问题,只不过数组可以循环来寻找。解决方法是,将两个相同的数组拼接,然后利用单调栈来寻找就好了(寻找过程与每日温度类似)。

java 复制代码
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] res = new int[nums.length];
        Arrays.fill(res, -1);
        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);

        // 拼接一个新的nums
        int[] nums2 = new int[nums.length + nums.length];
        for (int i = 0; i < nums.length; i++) {
            nums2[i] = nums[i];
            nums2[i + nums.length] = nums[i];
        }

        for (int i = 1; i < nums2.length; i++) {
            if (nums2[i] <= nums2[stack.peek()]) {
                stack.push(i);
            } else {
                while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
                    if (stack.peek() < nums.length) {
                        res[stack.peek()] = nums2[i];
                    } else {
                        res[stack.peek() - nums.length] = nums2[i];
                    }
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return res;
    }
}

其实也可以不扩充nums,而是在遍历的过程中模拟走了两遍nums。

java 复制代码
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] res = new int[nums.length];
        Arrays.fill(res, -1);
        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);
        
        for (int i = 1; i < nums.length * 2; i++) {
            // 模拟遍历两遍nums,注意一下都是用i % nums.length来操作
            if (nums[i % nums.length] <= nums[stack.peek()]) {
                stack.push(i % nums.length);
            } else {
                while (!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek()]) {
                    res[stack.peek()] = nums[i % nums.length];
                    stack.pop();
                }
                stack.push(i % nums.length);
            }
        }
        return res;
    }
}

相关推荐
bingbingyihao12 分钟前
多数据源 Demo
java·springboot
tainshuai1 小时前
用 KNN 算法解锁分类的奥秘:从电影类型到鸢尾花开
算法·分类·数据挖掘
在努力的前端小白5 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
Coovally AI模型快速验证7 小时前
农田扫描提速37%!基于检测置信度的无人机“智能抽查”路径规划,Coovally一键加速模型落地
深度学习·算法·yolo·计算机视觉·transformer·无人机
pusue_the_sun7 小时前
数据结构:二叉树oj练习
c语言·数据结构·算法·二叉树
一叶飘零_sweeeet7 小时前
从繁琐到优雅:Java Lambda 表达式全解析与实战指南
java·lambda·java8
RaymondZhao348 小时前
【全面推导】策略梯度算法:公式、偏差方差与进化
人工智能·深度学习·算法·机器学习·chatgpt
艾伦~耶格尔8 小时前
【集合框架LinkedList底层添加元素机制】
java·开发语言·学习·面试
zhangfeng11338 小时前
DBSCAN算法详解和参数优化,基于密度的空间聚类算法,特别擅长处理不规则形状的聚类和噪声数据
算法·机器学习·聚类
一只叫煤球的猫8 小时前
🕰 一个案例带你彻底搞懂延迟双删
java·后端·面试