209、长度最小的子数组
暴力解法
思路
第一眼初窥这道题,我并没有想到暴力解法。这道题的暴力解法也就是用嵌套的for循环,外层for循环遍历作为子数组的左边界i
,内层for循环遍历作为子数组的右边界j
,还要维护一个sum
变量用来求子数组的和,每次外循环的时候重新声明,每次内循环的时候sum++
,然后判断sum>=target
。
不难想到其时间复杂度是 O(n2)。
实现
//略
滑动窗口
在暴力解法的基础上,就可以很自然地想到,当j
在向右移动的过程中,首次满足了sum>=target
条件的时候,[i,j]
子数组肯定是此次外循环中长度最小的数组了,因为[i,j]<=[i,j+c]
,因此这时候就可以停下了。
同时我们发现,完全可以让i
在满足sum>=target
的条件下也跟着j
向右移动。这样的话,一个二维的for循环就被拍扁成为一维的for循环了,通过一层for循环即可控制i
、j
两个指针的变化。
实现
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int min = Integer.MAX_VALUE;
int i = 0, j = 0;
for (int sum = 0; j < nums.length; j++) {
sum += nums[j];
while (sum >= target) {
min = Math.min(min, j - i + 1);
sum -= nums[i++];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
//防止整个nums都不能满足target
}
}
59、螺旋矩阵II
思路
这道题不涉及到什么算法,单纯考察对二维数组下标的控制能力。
经过一段时间的观察不难发现,如果把整个螺旋过程看成转圈的话,每转一圈能填满4条边,2列2行,故而一个n
阶方阵要转n/2
圈才能填满。
需要注意的是,如果n
为奇数的话,那么就有一行一列需要单独画,也就是最中间的那个格子,同时也是最后一个填的格子,它的下标是[n/2][n/2]
。
这时候就可以对变量进行定义了,我们妨设当前是第offset
圈,那么offset
的范围就是[0,n/2)
,这样保证循环了n/2
次。
每一圈要拆分还要拆分为四条边,每条边分别用一个循环来遍历。如下所示是第offset
圈时的情况。
因此很容易就能写出四个for循环的初始值和终止条件了,但还要注意的是循环不变量分别是什么:
- 行:
offset
列:[offset,n-offset-1)
- 行:
[offset,n-offset-1)
列:n-offset-1
- 行:
n-offset-1
列:[offset,n-offset-1)
(递减) - 行:
[offset,n-offset-1)
(递减) 列:offset
有没有一种对称的美呢?🤫
实现
有了以上分析,代码就是手到擒来了
public int[][] generateMatrix(int n) {
int[][] ans = new int[n][n];
int count = 1;
for (int i = 0; i < n >> 1; i++) {
int border = n - i - 1;
for (int j = i; j < border; j++) {
ans[i][j] = count++;
}
for (int j = i; j < border; j++) {
ans[j][border] = count++;
}
for (int j = border; j > i; j--) {
ans[border][j] = count++;
}
for (int j = border; j > i; j--) {
ans[j][i] = count++;
}
}
if ((n & 1) == 1) ans[n >> 1][n >> 1] = count;
//填充中心格子
return ans;
}
总结
以上就是代码随想录算法训练营第二天的任务了。不得不说,以前还不信编码只是程序员很小的一部分工作,更多的是写文档,现在见识到了。如果说写代码花了一小时,那么写一篇博客就要花三小时。但是坚持写博客是有好处的,在写博客的过程中不仅分享了思路,更能帮助咱们缕清算法思维。
永远不要去觉得博客没人看就没必要写,因为只要你写了,那么你就没必要再去思考写这篇博客到底有没有必要了。