【前端学算法】以为是白给的困难题结果我白给了

前言

leetcode链接:

239. 滑动窗口最大值

实现声明~~(叠甲)~~,我只是个小菜鸡,leetcode总共就完成了100来题,大多数是简单的那种,遇到这题是因为我最近在刷滑动窗口的题目,我以为很简单,结果光速白给,大家看个乐子。

题目详情

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

返回 滑动窗口中的最大值

示例 1:

ini 复制代码
输入: 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

示例 2:

ini 复制代码
输入: nums = [1], k = 1
输出: [1]

白给暴力解法

第一想法是困难题就这?不就是左指针(i)遍历nums,然后右指针从(i -> i+k)编辑nums,记录每次遍历的最大值存起来,直接解决。

ts 复制代码
function maxSlidingWindow(nums: number[], k: number): number[] {
    let ans:number[] = [];

    for(let left = 0; left < nums.length - k + 1; left++) {
        let right = left;
        let tempAns = -Infinity;
        while(right < left + k) {
            tempAns = Math.max(tempAns, nums[right]);
            right++;
        }
        ans.push(tempAns);
    }

    return ans;
};

然后就直接白给了

失败总结

算了下,用例是长度为64317的数组,滑动窗口的长度是32879

在这段代码中,外层循环运行次数为 nums.length - k + 1 次,内层循环运行次数为 k 次。因此,总的运行次数为 (nums.length - k + 1) * k 次,那复杂度约为为O(n * k)

怪不得会超时,这段代码得优化到至少 O(n) 才能通过。

思考过程

1、如果要优化到 O(n) ,那么里面的那层k次循环的必须优化成 O(1)

2、那么我们就要思考这层内循环的意思是什么,这层内循环是为了获取滑动窗口内的最大值。

分类讨论

但是我们是否真的需要循环去遍历得到最大值?其实是不需要的,因为这里面有个规律,我们可以从示例中得到。

ini 复制代码
输入: 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

假设我们滑动窗口的边界坐标是[i, j],会获得一个最大值,我们记为 r0

然后接着开始滑动,窗口的边界坐标是[i+1, j+1],记移动后新出现的数是 n1,那么会有以下情况:

1、r0 > n1,且r0对应的坐标处于[i+1, j+1],那么此时的最大值r1 = r0

2、r0 > n1,且r0不处于[i+1, j+1],那么此时最大值为[i,j]之中非r0的数值n1之间产生;

3、r0 <= n1,那么此时的最大值r1 = n1;

构建递减数组:

1、3这种情况都一目明了,只有2需要思考一下怎么处理。

2这种情况的话,是一个数组中在O(1)时间内找到最大值,那么自然想到,如果这个数组是递减或递增的,那么从 0 或者 length-1 这两个位置就能拿到最大值。

那么如何构建这个这个玩意呢,假设我们构建的是 递减的 数组,那么我们需要做的事情就很明确:

每次push前,判断被push的元素是否比数组内的元素大,有的话,就把那些元素给踢出去。

那么,你就能得到一个递减的数组。

单调队列解法

ts 复制代码
function maxSlidingWindow(nums: number[], k: number): number[] {
    
    const result: number[] = [];
    
    //  存储坐标的单调队列
    //  为什么存坐标,是因为我们还要记录坐标是否越界了,就存nums[i]无法知道是否越界
    const queue:number[] = [];
    
    //  i可以理解为右指针
    for(let i = 0; i < nums.length; i++) {
        
        //  判断被push的元素是否比数组内的元素大,有的话,就把那些元素给踢出去
        //  这边从末尾踢是因为递减的,末尾元素一定是最小的,
        //  从小开始比较可以一直用queue.length-1作为比较坐标,不需要额外的变量去记录
        while (queue.length > 0 && nums[i] >= nums[queue[queue.length-1]]) {
            queue.pop();
        }

        queue.push(i);
        
        //  如果移动后最大值越界了,所以要把最大值踢出去
        if (i - queue[0] >= k) {
            queue.shift();
        }
        
        //  记录答案,右指针要大于等于k-1的时候才形成宽度为k的窗口
        if (i >= k-1) {
            //  队首就是最大值
            result.push(nums[queue[0]]);
        }
    }

    return result;
};

提交结果

结语

做了几道滑动窗口的题目后,有那么一点点感觉,但是又没有完全掌握,属实折磨,大概这就是之前一直不乐意刷leetcode的原因罢~~(自食其果)~~。

相关题目链接:

3. 无重复字符的最长子串

76. 最小覆盖子串

相关推荐
Coovally AI模型快速验证2 小时前
农田扫描提速37%!基于检测置信度的无人机“智能抽查”路径规划,Coovally一键加速模型落地
深度学习·算法·yolo·计算机视觉·transformer·无人机
pusue_the_sun2 小时前
数据结构:二叉树oj练习
c语言·数据结构·算法·二叉树
烛阴2 小时前
TypeScript高手密技:解密类型断言、非空断言与 `const` 断言
前端·javascript·typescript
RaymondZhao343 小时前
【全面推导】策略梯度算法:公式、偏差方差与进化
人工智能·深度学习·算法·机器学习·chatgpt
zhangfeng11333 小时前
DBSCAN算法详解和参数优化,基于密度的空间聚类算法,特别擅长处理不规则形状的聚类和噪声数据
算法·机器学习·聚类
圣保罗的大教堂4 小时前
leetcode 2348. 全 0 子数组的数目 中等
leetcode
啊阿狸不会拉杆4 小时前
《算法导论》第 32 章 - 字符串匹配
开发语言·c++·算法
小学生的信奥之路4 小时前
洛谷P3817题解:贪心算法解决糖果分配问题
c++·算法·贪心算法
你知道网上冲浪吗5 小时前
【原创理论】Stochastic Coupled Dyadic System (SCDS):一个用于两性关系动力学建模的随机耦合系统框架
python·算法·数学建模·数值分析
地平线开发者6 小时前
征程 6 | PTQ 精度调优辅助代码,总有你用得上的
算法·自动驾驶