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

前言

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. 最小覆盖子串

相关推荐
疯狂的沙粒12 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
莫叫石榴姐37 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
疯狂的沙粒39 分钟前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
茶猫_2 小时前
力扣面试题 - 25 二进制数转字符串
c语言·算法·leetcode·职场和发展
肥猪猪爸4 小时前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
readmancynn4 小时前
二分基本实现
数据结构·算法
萝卜兽编程4 小时前
优先级队列
c++·算法
盼海4 小时前
排序算法(四)--快速排序
数据结构·算法·排序算法
一直学习永不止步4 小时前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
张小小大智慧4 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript