随想录Day2|977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵Ⅱ
随想录Day2|977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵Ⅱ
977. 有序数组的平方
给定一个按非递减顺序的整数数组nums
,返回每个数字的平方组成的新数组,也要按照非递减顺序排序。
1 <= nums.length <= 104
-104 <= nums[i] <= 104
第一想法
不能原地操作了,因为负数的平方可能会更大,而排序算法可能会超时,所以需要在过程中就按照顺序填写。
哦,然后看到了说进阶是设计
如果我可以定位绝对值最小的元素作为开始,然后双指针往两边移动?首先扫一遍数组,从右往左,如果最右边是负数则直接以它为起点做平方,如果最右边是正数则……
woc等等啊,那为什么不从两边往中间啊,这样就不用找绝对值最小的元素位置了,只要最后两个指针重合了就好!因为两边一定是绝对值最大的。填入一个更大的平方值,移动那边的指针。
还需要注意一下细节,数组长度为偶数的时候不需要特别处理,长度为奇数的时候会遇到两个指针重叠的情况,应该在while (pleft < pright)
外面特判一下这种情况,把最后一个元素填入。
噫,好,我过了!
用时25min~
vector<int> sortedSquares(vector<int> &nums)
{
int len = nums.size();
int pleft = 0;
int pright = len - 1;
vector<int> result_nums(len, 0);
int pres = len;
while (pleft < pright)
{
int numl = nums[pleft];
numl *= numl;
int numr = nums[pright];
numr *= numr;
// 填入更大的那个,并向中间移动对应的指针
if (numl > numr)
{
result_nums[--pres] = numl;
pleft++;
}
else
{
result_nums[--pres] = numr;
pright--;
}
}
if (pleft == pright)
result_nums[--pres] = nums[pleft] * nums[pleft];
return result_nums;
}
209. 长度最小的子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
第一想法和纠正
没错,又是看了卡尔的题目建议,大概知道方向,滑动窗口法。感性地体会一下,如果一段数中有很多大数字,但是夹杂了很多负数,就可能会很长;不怎么含有负数,但是都是小的正数,也可能会很长。(哪里有负数了看看数据范围!你到底审不审题的啊啊啊 发出尖锐爆鸣声*)
盲猜需要两个指针,指向这个滑动窗口的左右两边。接下来就要确定这个窗口的规则了(啊这时候我的思维飘到了动态规划),左指针起始在0位置,右指针往右走并且计算当前窗口内的和。应该会有一个全局的min_len
记录能满足条件的子数组的最短长度,每当当前的子数组和大于target
,就右移左指针,直到恰好不满足条件,记录那个临界长度。然后右指针继续往右走……
啊不过,如果左指针再右移一点又符合了,这种可能性有吗?那也就是说,子数组和先降后升,左边先滑走了一个正数,然后又滑走了一个负数,而且负数的绝对值更大。对,所以要是说起负数在的情况,滑动窗口这种想法就不适用了吧!
不确定上面想法的严谨性,不过还是想想怎么判断不存在符合条件的子数组吧?那么就是min_len
的起始值设置为n+1
,这样如果结束循环之后还是这个值,就返回0。循环的条件应该是右指针到达最右边。
好,不管怎么样,先试试吧。
好的果然过不了case!
看完随想录题解的想法
右移左指针时,需要在for循环内部引入一个while循环(我写成if了难怪错了)!不断比较target
和当前子数组和的大小关系,(更新cur_len
),更新min_len
,然后把左指针右移一位。
还有一个小错误是我之前在右移左指针时更新的是min_len
,但是正确的流程应该是记录cur_len
,然后每次cur_len
可能变小的时候(所以只在左指针右移的时候判断,因为右指针右移肯定cur_len
变大)判断cur_len
如果比min_len
小,再去更新min_len
。
最后整理一下直观感受,这个窗口始终是“紧”的,右移右指针是扩展窗口,右移左指针是缩紧窗口。只有在不满足target
条件的时候才扩展,一旦满足了马上试试能不能缩紧(while保证了缩到最紧),并且一直更新全部历史记录中的最小窗口长度。这样就可以保证我们找到全局最小子数组长度。
用时40min~
int minSubArrayLen(int target, vector<int> &nums)
{
int n = nums.size();
int min_len = n + 1;
int cur_len = 0;
int pleft = 0;
int pright = 0;
int cur_sum = 0;
for (; pright < n; pright++)
{
cur_sum += nums[pright];
while (cur_sum >= target)
{
cur_len = pright - pleft + 1;
min_len = min_len < cur_len ? min_len : cur_len;
// pleft begin to move right
cur_sum -= nums[pleft++];
}
}
if (min_len == n + 1)
min_len = 0;
return min_len;
}
59. 螺旋矩阵Ⅱ
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
第一想法
按照 → ↓ ← ↑ 的顺序循环,主要是计算清楚每次走多少步。把每一行的管辖范围都控制为左开右闭,也就是说,对一个矩形框,如果是
矩阵是
思路详解
真不错,我和卡尔的思路还不一样,不过我觉得自己这个真的挺简洁的!
cur_len
代表当前四个方向走的边长,如果cur_len = 2
,下一轮再减2就会为0,实际上就是最中心那个数。它的行列号都相等,(n - 1) / 2
,在最后判断一下如果cur_len == 0
,手动填入cur_len = 1
,可以正常填写,下一轮再减2就会为-1,则while循环的条件应该是cur_len > 0
,这样奇偶都能正常停止循环。
每次向右走之前,由于新的一轮是走靠里一点的小一圈的框,起始位置也要调整,所以需要i++; j++;
。应该在进入循环之前把初始值都设为-1,以便从(0, 0)开始,第二轮就是从(1, 1)开始,以此类推。
用时40min~
vector<vector<int>> generateMatrix(int n)
{
vector<vector<int>> nums(n, vector<int>(n, 0));
int n_square = n * n;
int cur_len = n - 1;
int cur_num = 1;
int i = -1, j = -1;
while (cur_len > 0)
{
int steps;
i++;
j++;
for (steps = 0; steps < cur_len; steps++, j++)
nums[i][j] = cur_num++;
for (steps = 0; steps < cur_len; steps++, i++)
nums[i][j] = cur_num++;
for (steps = 0; steps < cur_len; steps++, j--)
nums[i][j] = cur_num++;
for (steps = 0; steps < cur_len; steps++, i--)
nums[i][j] = cur_num++;
cur_len -= 2;
}
if (cur_len == 0)
{
int mid = (n - 1) / 2;
nums[mid][mid] = n_square;
}
return nums;
}
碎碎念
今天的状态真的不错,满课一天所以拖到晚上才写,但是还是很在状态的!计时来自插件自带,是我打开题目到AC的总用时,做题中途我也在写这个文档的内容,是我整理思路的一种方式。
成就感真的很管饱!明天也要继续加油!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了