997.有序数组的平方和
给你一个按非递减顺序排序的整数数组 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. 暴力解
思路
先平方,后排序
复杂度分析
- 时间复杂度
O(nlogn)
- 空间复杂度
O(nlogn)
2.双指针归并
思路
- 找到负数与正数的分界线,将原数组逻辑上划分为两个有序数组
- 用双指针指向逻辑上的两个有序数组首
- 对逻辑上的两个有序数组进行一次归并
复杂度分析
- 时间复杂度
O(n)
- 空间复杂度
O(1)
代码
class Solution {
public:
//双指针归并法
vector<int> sortedSquares(vector<int>& nums) {
int k=0; //标记负数与正数的分界线
int n=nums.size();
vector<int> ans(n);
//将所有数字求平方
for(int i=0;i<n;i++){
if(nums[i]<=0) k++;
nums[i]=nums[i]*nums[i];
}
//双指针归并排序
int left=k-1,right=k,j;
cout<<left<<" "<<right<<endl;
for(j=0;left>=0&&right<n;j++){
if(nums[left]<nums[right])
ans[j]=nums[left--];
else
ans[j]=nums[right++];
}
while(left>=0) ans[j++]=nums[left--];
while(right<n) ans[j++]=nums[right++];
return ans;
}
};
3. 双指针法
思路
- 双指针分别指向数组首和尾,并向中间遍历
- 每次比较两个指针指向的值,将较大的移入新数组尾
复杂度分析
- 时间复杂度
O(n)
- 空间复杂度
O(1)
代码
class Solution {
public:
//双指针法
vector<int> sortedSquares(vector<int>& nums) {
int k=nums.size()-1;
vector<int> ans(k+1);
for(int left=0,right=k;left<=right;){
if(nums[left]*nums[left]>nums[right]*nums[right]){
ans[k--]=nums[left]*nums[left];
left++;
}
else{
ans[k--]=nums[right]*nums[right];
right--;
}
}
return ans;
}
};
4. 总结
第二种和第三种解法的区别是前者是中间向两头归并,后者是两头向中间归并。由于后者不需要找分界线,因此比前者更简洁。
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
解题关键:滑动窗口的循环表示终止位置,根据子序列动态调节滑动窗口的起始位置
1. 暴力解(力扣超时)
思路
两层for循环,外层遍历,内层从当前位置寻找满足条件的子数组
复杂度分析
- 时间复杂度
O(n^2)
- 空间复杂度
O(1)
代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
//暴力法
int n=nums.size(),min=target,flag=0; //int min=INT_MAX;
for(int i=0;i<n;i++){
int count=0,sum=0;
for(int j=i;j<n;j++){
sum+=nums[j];
count++;
if(sum>=target&&min>count) {
min=count; //count可改为j-i+1
flag=1;
break;
}
}
}
if(flag==0) min=0;
return min; //return min==INT_MAX?0:min;
}
};
2. 滑动窗口
思路
for
循环表示滑动窗口j
终止位置while
内层循环表示动态调节滑动窗口i
起始位置- 当子序列的和大于目标值时,比较子序列长度与当前最小的子序列长度,并更新滑动窗口
i
起始位置
复杂度分析
- 时间复杂度
O(n)
(每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)) - 空间复杂度
O(1)
代码
class Solution {
public:
//滑动窗口
int minSubArrayLen(int target, vector<int>& nums) {
int result=INT_MAX;
int sum=0;//滑动窗口的数值之和
int i=0;//滑动窗口的起始位置
for(int j=0;j<nums.size();j++){
sum+=nums[j];
//while每次更新起始位置i,并不断比较子序列是否符合条件
while(sum>=target){
result=min(result,j-i+1); //保留长度较小的结果
sum-=nums[i++]; //动态更新滑动窗口起始位置
}
}
return result==INT_MAX?0:result;
}
};
59. 螺旋矩阵II
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
解题关键:循环不变量
思路
- 转圈的逻辑:外圈和内圈的转圈规则相同
- 循环不变量
复杂度分析
- 时间复杂度
O(n^2)
- 空间复杂度
O(1)
代码
- 我的版本
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int low=0,high=n,count=1; //low,high控制圈的大小
vector<vector<int>> matrix(n, vector<int>(n, 0));
while(count<=n*n){
int row,col;//row,col负责遍历矩阵赋值
row=low;col=low;
while(col<high) matrix[row][col++]=count++;
col--;row++;
while(row<high) matrix[row++][col]=count++;
row--;col--;high--;
while(col>=low) matrix[row][col--]=count++;
col++;row--;low++;
while(row>=low) matrix[row--][col]=count++;
}
return matrix;
}
};
- 代码随想录版-循环不变量左闭右开法
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int startx=0,starty=0,count=1;
int i,j;
int offset=1;
int loop=n/2;
int mid=n/2;
vector<vector<int>> matrix(n, vector<int>(n, 0));
while(loop--){
i=startx;
j=starty;
for(j=starty;j<n-offset;j++)
matrix[startx][j]=count++;
for(i=startx;i<n-offset;i++)
matrix[i][j]=count++;
for(;j>starty;j--)
matrix[i][j]=count++;
for(;i>startx;i--)
matrix[i][j]=count++;
//更改起始位置坐标
startx++;
starty++;
//更改圈的大小
offset++;
}
if(n%2!=0) matrix[mid][mid]=n*n; //填充中间值
return matrix;
}
};
for(;i>startx;i--)
matrix[i][j]=count++;
//更改起始位置坐标
startx++;
starty++;
//更改圈的大小
offset++;
}
if(n%2!=0) matrix[mid][mid]=n*n; //填充中间值
return matrix;
}
};