数组DAY02(12.04)

30 阅读4分钟

209.长度最小的子数组

1. 资源

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。

题目链接:leetcode.cn/problems/mi…

文章讲解:programmercarl.com/0209.%E9%95…

视频讲解:www.bilibili.com/video/BV1tZ…

2. 个人总结

2.1. 算法原理

在数组中维护一段“连续区间”的统计信息,并通过左右指针移动来更新区间,使算法从 O(n²) 变成 O(n)。

  1. 举个最简单的例子:

找数组中连续长度为 3 的子数组的最大和。

你当然可以写三层循环不停算,但很慢。

滑动窗口的想法是:

“窗口右边加进一个元素,同时让窗口左边丢掉一个元素。”

你就可以用 O(1) 的代价更新窗口的和,而不是从头算和。

  1. 核心流程(通用)

可以总结为 4 步:

  1. 右指针右移:让窗口扩大
  2. 更新窗口内部记录(和、频率等)
  3. 当窗口不满足题目条件时
    移动左指针收缩窗口
  4. 此时窗口满足条件
    → 记录结果(最大/最小/计数等)
2.2. 应用前提

前提 1:问题必须基于 “连续区间”

滑动窗口的窗口是一段 连续的子数组/子串

所以它只能解决:

连续子数组

连续子串

连续序列

前提 2:窗口的统计信息能通过“增量更新”维护

也就是:

当右边进一个元素、左边出一个元素时,
你能快速(O(1))更新窗口状态,而不是重新计算整个区间。

比如:

  • 求和:sum += 新元素 - 老元素
  • 求频率:freq[x]++ / freq[y]--
  • 判断是否满足条件:哈希表 可 O(1) 判断
  • 最小值/最大值:双端队列维护

前提 3:窗口扩张/收缩必须是“单调移动”

左右指针必须满足:

  • 右指针只往右走,不回头
  • 左指针也只往右走,不回头

如果解题需要指针来回跳,就不适合。

这样整个算法才是 O(n)。

2.3. 注意点
  1. 外层遍历的指针到底是窗口的终止位置指针还是起始位置指针

应该是终止位置。先起始位置不变,通过不断右移终止位置,拉长区间,一旦区间内统计值满足要求就停止,再左移区间起始位置,使在当前情况下,最小区间满足要求。在向右移动区间,重复上述过程,期望有更短的区间存在。

2.4. 代码:

暴力解法

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int minSize = n + 1;

        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < n; j++) {
                sum += nums[j];
                if (sum >= target) {
                    minSize = min(minSize, j - i + 1);
                    break; // 第一个满足的 j 即为该起点的最短长度
                }
            }
        }
        return minSize == n + 1 ? 0 : minSize;
    }
};

滑动窗口 (双指针)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int reuslt = nums.size() + 1;
        int i = 0; //指向窗口起始位置
        int sum = 0;
        for(int j = 0;j<nums.size();j++)
        {//指向窗口终止位置
            //获得窗口内元素之和
            sum += nums[j];
            while(sum >= target)
            {//合适的窗口大小
                reuslt = min(reuslt, j-i +1);
                //移动窗口起始位置,再循环下一次,判断缩小窗口后是否还满足
                sum = sum - nums[i];
                i++;
            }
            //找到了一个最小窗口大小,但是还没遍历完,窗口终止位置继续右移,重复上述过程
        }
        return reuslt == nums.size()+1 ? 0 : reuslt; 
    }
};

59.螺旋矩阵II

1. 资源

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。 

题目链接:leetcode.cn/problems/sp…

文章讲解:programmercarl.com/0059.%E8%9E…

视频讲解:www.bilibili.com/video/BV1SL…

2. 个人总结

这部分目前来看就是心里明白,一写就乱,这部分没什么算法可讲,就是自己推。得保证自己的边界处理正确,其间不会思维混乱(真的很麻烦,写着写着就忘乎所以了)

2.1. 注意点

注意,边界一致原则,注意转每圈的四个临界点。视频里面采用的方式是 左闭右开。遍历第一行最右边的点不处理;遍历右边第一列,最下面的点不处理;遍历底边,从右到左,最后一个点(最左边点不处理)

2.2. 代码