第二天打卡,题目单静静躺在收藏夹里:209.长度最小的子数组、59.螺旋矩阵II。第一天刚被704二分查找和27移除元素虐过,我以为第二天就算不上新知识点,至少也该进入链表或者哈希表,结果一看还是数组。说句实话,我心里甚至有点轻敌,觉得“数组这章还能翻出什么花来”。
然后两道题加起来耗了我整整三个多小时。209卡在“while写成了if”这种看似弱智的问题上,59螺旋矩阵II更是被n=3的边界绕得团团转。等终于AC完再回头看代码随想录的题解,才发现这两道题放在第二天不是没道理的:滑动窗口是双指针思想的延伸,螺旋矩阵则是“模拟类题目”的入门代表。
这篇文章我把自己的完整复盘写下来,包括暴力思路为什么不行、滑动窗口为什么能行、螺旋矩阵的边界条件怎么才能不靠玄学写对,以及我在本地调试时打印出来的那些坑。如果你正在跟代码随想录打卡、准备面试刷题,或者单纯被数组题折磨到怀疑人生,这篇就当一份排雷笔记看吧。
1. 两道题的正确打开方式:先搞清楚“考点”再动手
1.1 209题考核的是什么
209题全称是“长度最小的子数组”,LeetCode原题,题面很简单:给定一个含有 n 个正整数的数组和一个正整数 target,找出该数组中满足其和大于等于 target 的长度最小的连续子数组,并返回其长度;如果不存在符合条件的子数组,返回0。
这里有两个关键词值得圈出来:第一是“连续”,第二是“正整数”。
连续意味着我们不能对数组排序,排序会直接破坏子数组的连续性。很多第一次做这题的人第一反应是“窗口固定长度挨个滑一下”,或者是“双指针往中间夹逼”,但真正走通之后会发现,正确解法其实是双指针里面更进阶的一种形态——滑动窗口。
为什么说它进阶?因为传统双指针通常是比较两个独立指针对应的值,而滑动窗口是把 right 指针不断向右扩张,把 left 指针按需向右收缩,整个窗口维护的是“当前这一段连续子数组的和”。我在第一天做27移除元素时已经用过双指针,那是一种“快指针找目标、慢指针写结果”的结构,到了209这里则是“right负责扩张、left负责收缩”,两种双指针的移动逻辑完全不一样。
1.2 59题考核的是什么
59螺旋矩阵II同样来自LeetCode:给定一个正整数 n,生成一个包含 1 到 n² 所有元素的 n x n 正方形矩阵,元素按顺时针螺旋顺序排列。
比如 n=3,输出是:
text复制1 2 3
8 9 4
7 6 5
这题不考什么高深算法,但它是面试高频的“模拟题”入门代表。模拟题的本质是“用代码一步一步翻译题面的过程”,最考验人的是对循环边界的控制能力。
很多新手拿到螺旋矩阵,脑子里第一反应是“按层循环”:最外面一圈、然后第二圈……这个方向是对的,但实际写起来问题一大堆:每条边遍历到哪里停?奇数n的最后中心一格谁来填?用左闭右开还是左闭右闭?这些问题如果不先在纸上推一遍,写出来的代码八成会数组越界或者死循环。我自己的经验是:螺旋矩阵这类题,越是觉得“这不就模拟嘛”,越容易栽在边界上。
1.3 我为什么用C++做复盘
代码随想录的训练营本身是多语言友好的,我用的是C++写题,后续实现代码都以C++给出。选择C++没有任何“优越”的意思,主要是因为我日常刷题环境就偏向C++,而且它的数组和vector边界行为足够直接,很适合讲这类边界题。顺手说明一下:用Java、Python、Go实现时,思路完全一样,唯一要注意的是Python里列表的初始化写法不一样,别直接套C++的二维数组初始化方式。
做题环境依然是本地IDE + LeetCode提交。我会在IDE里先写好测试用例,再把函数粘贴到LeetCode。这个习惯从第一天开始就养成了,因为LeetCode自带控制台在调试循环边界问题时其实不太好用,本地打印反而更快。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 209长度最小的子数组:从暴力到滑动窗口的完整推导
2.1 为什么暴力双循环不适合作为最终答案
先把暴力解法写出来,它是理解后续优化的基础。最直觉的做法是枚举所有可能的左边界,然后让右边界从左边开始向右移动,不断累加元素和,一旦和达到 target 就记录长度并 break,再换下一个左边界继续。
C++版本暴力如下:
cpp复制class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int result = INT_MAX;
for (int i = 0; i < n; i++) {
int sum = 0;
for (int j = i; j <
