代码随想录训练营打卡DAY 2 | 双指针+滑动窗口+螺旋矩阵
双指针
题目描述
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
题解
思路:看示例就很清楚了,可以直接每个都平方后再进行排序,暴力解法时间复杂度为O(nlogn)
笔者看到这里并不想直接暴力解,这个数组本身就是有序的;如果直接暴力解,就忽略了有序这个性质。
可以清楚地看到,要构造新数组,要么找到绝对值最小的数,从该点开始,双指针相背遍历;要么从数组两边开始,相向遍历。显然前者需要先找到绝对值最小的数,耗费时间更多,选取后者。
双指针法需要创建一个长度与nums相同的数组,笔者参考代码随想录文档后并没有找到原地执行的方法。
核心代码【C++】:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> newArray(nums.size());
// 相向指针,左指针left,右指针right
int left = 0, right = nums.size() - 1;
int left_2,right_2;
// 左右指针相向遍历,平方值大的填入新数组中,且新数组是从右到左填充
for(int i = nums.size() - 1; i >= 0; i--){
left_2 = nums[left] * nums[left];// 左值平方
right_2 = nums[right] * nums[right];// 右值平方
if(left_2 >= right_2){
newArray[i] = left_2;
left++;
}
else{
newArray[i] = right_2;
right--;
}
}
return newArray;
}
时间复杂度:O(n)
空间复杂度:O(n)
滑动窗口
题目描述
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例2:
输入:target = 4, nums = [1,4,4]
输出:1
示例3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
- 1 <= target <= 109
- 1 <= nums.length <= 105
- 1 <= nums[i] <= 105
题解
思路一:笔者第一时间连暴力求解都没有想到,脑袋里都是窗口滑来滑去;参考文档后发现可以使用暴力法求解,遂自行敲下了暴力解法。
核心代码【C++】:
int minSubArrayLen(int target, vector<int>& nums) {
int i,j;
int n = nums.size();
int min = n;// 记录子串最小长度
int sum = 0;
for(i = 0; i < n; i++){
sum += nums[i];
}
if(sum < target) return 0;// 没有符合要求的子串
// 暴力求解,力扣超时不通过
for(i = 0; i < n; i++){
sum = 0;
for(j = i; j < n; j++){
sum += nums[j];
if(sum >= target && (j-i+1) < min){
min = j-i+1;
break;// 这里其实只要找到符合条件的就可以跳出内层循环,笔者一开始没有写break;
}
}
}
return min;
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
思路二:滑动窗口
本题的滑动窗口长度是动态变化的,覆盖的位置也是动态变化的。这里就需要控制变量,变化窗口左端的时候右端不能变,反之亦然。窗口移动操作都是从左到右,过程如下:
- 先移动窗口右端,直到窗口内包含的数值>target(for循环实现)
- 接着移动窗口左端,直到窗口满足>target并且长度最小(while循环实现)
- 重复以上操作,记录下子串最小长度subLength
由以上过程可知,每个元素最多进入窗口一次,最多移出窗口一次,所以时间复杂度就是2*n,即 O ( n ) O(n) O(n)。
核心代码【C++】:
int minSubArrayLen(int target, vector<int>& nums) {
/*
滑动窗口,本题是长度变化的窗口,每次只能变化窗口的一端不能同时变化
1.先移动窗口右端,直到窗口内包含的数值>target(for循环实现)
2.接着移动窗口左端,直到窗口满足>target并且长度最小(while循环实现)
3.重复以上操作,记录下子串最小长度subLength
*/
int result = INT32_MAX;
int i = 0, j;// i为窗口左端,j为窗口右端
int sum = 0;
int subLength;
for(j = 0; j < nums.size(); j++){// 移动窗口右端
sum += nums[j];
while(sum >= target){// 移动窗口左端
sum -= nums[i++];
subLength = j - i + 2;// 最后一次进循环后i本不该自增,但是上面++了,所以这里不是j - i + 1而是j - i + 2
result = result > subLength ? subLength : result;
// 其实可以将sum -= nums[i++];写在最后,此时subLength = j - i + 1
}
}
return result == INT32_MAX ? 0 : result;
}
时间复杂度:O(n)
空间复杂度:O(1)
螺旋矩阵
题目描述
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
题解
思路:结合代码随想录文档提及的区间定义,笔者考虑到可以使用左闭右开的形式,直接这样说可能比较抽象。见下图:
- 第一次循环区间为 j = [0,3)
- 第二次循环区间为 i = [0,3)
- 第三次循环区间为 j = [3,0),这里为倒序,即 j 取3,2,1
- 第四次循环区间为 i = [3,0),这里为倒序,即 i 取3,2,1
- 第五次循环区间为 j = [1,2)
- 第六次循环区间为 i = [1,2)
- 第七次循环区间为 j = [2,1),这里为倒序,即 j 取2
- 第八次循环区间为 i = [2,1),这里为倒序,即 i 取2
既然循环区间大致知道如何了,接下来看看细节,每次循环结束后,由于i,j自增操作,指针会自动跳到下一个。同时设置一个自增变量k(图中1-16即k的值),每次指针移动一次便赋值给相应元素,k初值为1。
-
解释:图中的 k 所在位置表示 k 的值,如该图表示 k 从1自增到4
-
第一次循环结束后,指针刚好顺到下一循环区间的左端点,而k也自增到4,刚好可以填入到该位置。
-
4次循环结束(即遍历一圈)后,可以看到指针由于i–跑到了[0][0]位置,此时就需要将其移动到下一圈的开始,只要i++和j++即可。
-
遍历完一圈后,k仍自增,需要填到[1][1]位置,经过上一步提及的i++和j++以后,指针和k值再次对应上。
-
一圈结束后,下一圈的长度变小,所以设置offset来记录每个循环的次数。
这里需要注意一个问题,当n为奇数时,会陷入死循环,设置一个if语句打破死循环,最后还需要将k=n*n赋值到相应位置(详见代码)。
核心代码【C++】:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n,vector<int>(n));
int i = 0, j = 0;
int k = 1;
int offset = n - 1;
while(k <= n*n){// n为偶数时正常运行,当n为奇数时,最后一个数k == n*n会陷入死循环
if(k == n*n && k%2 == 1) break;//打破奇数导致的死循环
while(k <= n*n && j < offset){
matrix[i][j++] = k;
k++;// 这里k++可以合并到上一行吗?
}
while(k <= n*n && i < offset){
matrix[i++][j] = k;
k++;
}
while(k <= n*n && j >= n - offset){
matrix[i][j--] = k;
k++;
}
while(k <= n*n && i >= n - offset){
matrix[i--][j] = k;
k++;
}
i++;j++;//将指针放到下一圈的起点
offset--;//笔者第一想法是offset -= 2;但是其实有问题,因为下一圈开始的下标是上一圈开始下表i++和j++
}
// n为奇数时需要处理最后一个数字
if(n%2 == 1) matrix[i][j] = k;
return matrix;
}
模拟螺旋矩阵过程,需遍历完整个n*n矩阵,故时间复杂度为 O ( n 2 ) O(n^2) O(n2)
参考文档
- https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
- https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
- https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html