今日更新题目:977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
977.有序数组的平方
给你一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
示例 1:
- 输入:nums = [-4,-1,0,3,10]
- 输出:[0,1,9,16,100]
- 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2:
- 输入:nums = [-7,-3,2,3,11]
- 输出:[4,9,9,49,121]
提示:
- 1 <= nums.length <= 104
- -104 <= nums[i] <= 104
- nums 已按 非递减顺序 排序
思路
提示中说到:nums 已按非递减顺序排序,且每个数字的平方均为正数,我的第一想法就是双指针法,很多人第一次见可能会想不出,所以在首次接触到一个新方法只需要理解并接受就好,而我是第二次见了,则应该在双指针法的基础上进一步想:两个指针从哪出发,沿着什么方向,两个指针分别在什么时候更新。下面是我有过的一些想法:
- 先让快指针走到第一个大于等于0的位置,然后计算快慢指针所指数字的平方,比较后自增即可(看似有效,但忽略了返回的数组也要有序,按照示例1,若此时快慢指针分别位于nums[0]=-4,nums[2]=0,实际上是会出错的,原因在于要想返回的数组有序,则两个指针应在两端向中间靠近,或者是两个指针应该在nums中平方值最小的位置向两端出发,即都是较大值比较或较小值比,但是从中间出发又显得没必要,因为你要先找到nums中最接近0的数,那不如直接两边向中间靠近)
- 两个指针从两端向中间靠近,谁索引对应的平方值更大谁更新,而返回数组则从末尾处开始更新
class Solution {
public:
// 时间复杂度为O(n)
vector<int> sortedSquares(vector<int>& nums) {
int k = nums.size() - 1;
vector<int> result(nums.size(), 0);
for(int left = 0, right = nums.size() - 1; left <= right; ){
if(nums[left] * nums[left] <= nums[right] * nums[right]){
result[k--] = nums[right] * nums[right];
right--;
}
else{
result[k--] = nums[left] * nums[left];
left++;
}
}
return result;
}
};
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
- 输入:s = 7, nums = [2,3,1,2,4,3]
- 输出:2
- 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
- 1 <= target <= 109
- 1 <= nums.length <= 105
- 1 <= nums[i] <= 105
思路
最好想的方法当然是两层for循环,复杂度为 O(n2),在每个位置尝试不同长度的子数组组合
其实审题发现,题目要求找出长度最小的连续子数组,重点在于连续,也就是有可能通过一次遍历完成,可以用滑动窗口来实现(第一次见的时候想破脑袋都想不到),那滑动窗口起始位置,结束位置该如何更新呢,for循环索引表示的是窗口的起始位置还是结束位置呢?实际上把问题转换一下就是,起始位置更新代表窗口缩小,结束位置更新代表窗口扩大,为了找出最小子数组,那自然是:
- 窗口子数组之和 ≥ s 的时候要尝试缩小,试着找到更小的子数组;
- 窗口子数组之和 < s 的时候要尝试扩大,试着找到勉强符合要求的子数组;
- 至于谁是for循环里的索引,先进入for循环的当然是最先出发的,也是最先遍历完数组的,那自然是结束位置是for循环的索引。
class Solution {
public:
// 时间复杂度:O(n),空间复杂度:O(1)
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0;
int sublength = 0;
int left = 0;
//滑动窗口的本质也是双指针
for(int right = 0; right < nums.size(); right++){
sum += nums[right];
//第一次找到大于target的字串时,还不能确定是最小字串,要遍历完整个数组
//此时应该缩小窗口,若缩小窗口后仍大于target,则窗口继续缩小(left--)
//若缩小窗口后小于target,则应该让窗口扩大(right++)
while(sum >= target){
sublength = right - left + 1;
result = result < sublength ? result : sublength;
sum -= nums[left++];
}
}
return result < INT32_MAX ? result : 0;
}
};
59.螺旋矩阵II
给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
思路
这题似乎没有什么特别的算法,总的来说就是把循环过程分解实现即可,弄清楚边界即可,由于我个人不习惯去算 loop,还要用一个 offset 控制四个方向的填充,所以本题没采用代码随想录的解法,使用了 leetcode 题解里的精选解法,一图胜千言,具体思路如下,现有矩阵mat[i][j]
,模拟顺时针向内填充:
- 从左到右,
mat[i][j]
的 i 不变,j 变,起始位置是 left,结束位置是 right,填充后上边界缩小,即top++
- 从右到下,
mat[i][j]
的 i 变,j 不变,起始位置是 top,结束位置是 button,填充后右边界缩小,即right–
- 从右到左,
mat[i][j]
的 i 不变,j 变,起始位置是 right,结束位置是 left,填充后下边界缩小,即button--
- 从下到上,
mat[i][j]
的 i 变,j 不变,起始位置是 button,结束位置是 top,填充后左边界缩小,即left++
照着上面规则将其填入for循环即可,循环变量,起始位置,结束位置都给出了,然后再慢慢补充细节,总共 n2 步,走完即可。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n, vector<int>(n, 0));
int left = 0, right = n - 1, top = 0, button = n - 1;
int step = 1;
while(step <= n*n){
// left to right
for(int j = left; j <= right; j++)
matrix[top][j] = step++;
top++;
// top to button
for(int i = top; i <= button; i++)
matrix[i][right] = step++;
right--;
// right to left
for(int j = right; j >= left; j--)
matrix[button][j] = step++;
button--;
// bottom to top
for(int i = button; i >= top; i--)
matrix[i][left] = step++;
left++;
}
return matrix;
}
};