LeetCode977有序数组的平方
初印象
-
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
-
问题拆解,先平方后排序。注意负数的平方可能和正数平方相等。初步判断暴力解法时间复杂度O(nlogn)?
-
暴力排序解法:借助C++ sort函数实现暴力排序,需要加入头文件#include,是一种类快排的排序算法,时间复杂度O(logn)。用法详见链接。
-
原来原数组是有序的,由于负数平方后可能超过部分正数,所以数组平方后的最大值(相对最大)只可能出现在两端。所以可以用双指针法。
-
双指针法建立一个新数组,一个指针k指向新数组末端,从尾部向头部逐个移动填写挨个填写最大值。两个指针i、j指向原数组两端,分别向中间移动进行比较,较大值填向k的位置。
看视频讲解后的想法
-
for循环i<=j,否则丢项。i++和j–不写进循环体,在语法上是可以的。为什么不写进去?因为我们的i++和j–是有条件的。(用while循环也可以)。
-
nums[i], nums[j]相等的情况下移动谁都是一样的,所以if后面可以直接用else,即两种情况就能覆盖。
实现过程中遇到的问题
一、暴力解法
//有序数组的平方——暴力解法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i=0;i<nums.size();i++){
nums[i]=nums[i]*nums[i];
}
sort(nums.begin(),nums.end());
return(nums);
}
};
- line8 sort函数要填排序的起始和结束位置,用数组的begin和end
- line9 返回一个数组
二、双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int k=nums.size()-1;
vector<int>result(nums.size(),0);//定义一个size大小相等,初始值为0的数组
for(int i=0,j=nums.size()-1;i<=j;){
if(nums[i]*nums[i]<=nums[j]*nums[j]){
result[k--]=nums[j]*nums[j];
j--;
}else{
result[k--]=nums[i]*nums[i];
i++;
}
}
return(result);//函数返回一个数组
}
};
vector用法总结:vector用法详细总结
vector与数组的区别: vector与数组
总结
注意到虽然负数平方后可能超过正数,但在原数组单调不减的情况下,平方后的数组中较大值的出现仅能出现在数组两端。所以可以使用双指针法。时间复杂度和空间复杂度都是O(n)。
LeetCode209长度最小的数组
初印象
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和≥ target 的 长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0。
-
没说有序,需要一个sum和target比较。
-
只需返回长度。需要长度最小的。
-
当且仅当每一位都大于target才不存在符合条件的子数组?
-
大体思路,
双指针一个i记录起始位置,一个j记录结束位置,一个sum一个length。i如果大于target直接后移,j从i开始移动每移动一位就sum加一位的数值,直到sum>target(之后i开始后移)或者j已到数组末位。当i移动到末尾而非j移动到数组末尾时循环结束。 -
沃柑是sum>target。上面的思路和暴力法无异。因为先确定的是头部再去移动尾部,还是会陷入两个for循环的怪圈。
-
用j先向后移动然后用i去追。可以用一个for循环解决问题。
-
滑动窗口法:
-
窗口是什么?窗口是满足sum>target的连续子数组
-
窗口前端什么时候移动?当sum>target时,向末端移动缩短窗口长度,寻找最小子数组
-
窗口末端什么时候移动?当sum<=target时末端向后移动,然后换窗口前端移动
-
窗口前端是从数组最前端开始移动吗?是的
看讲解后的想法
- 沃柑是sum>target。上面的思路和暴力法无异。
- while循环
实现过程中的问题
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i=0;
int sum=0;
int length=0;
int result = INT32_MAX;//result初始值不能设置为0,否则永远不行
for(int j=0;j<nums.size();j++){
sum+=nums[j];
/*length++;*/
while(sum>=target){//大于等于而非大于
length=(j-i+1);
result=length<result?length:result;
sum-=nums[i++];//注意这几条的顺序
}
}
return result==INT32_MAX?0:result;//没找到合适的子序列,result还为初始值,返回0
}
};
- 局部变量:循环体内的变量
- line7result不能习惯性设置为0,否则怎么和result比较呢?
- line10没有用这种length计算方式
- line11要用大于等于,虽然是正整数数组,好像等于的情况下i不用再移动了,但是
如2 3 1 2 4 3中一样,1+2+4的后面可能藏着4+3这种相等的情况。 - line15 i++出现,这条放最后面
- line18 考虑到没有找到的情况要这样写
总结
这道题双指针法还是十分巧妙,应该跟着代码手写一下算法过程,体会指针移动的情况。
LeetCode59螺旋矩阵
初印象
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
-
我测好难的题,输入3的话就是3 * 3的矩阵,4是4 * 4的矩阵。还要转弯,要按大小顺序输入吗?最后一个数n2 出现的位置貌似无法确定。
-
要按大小顺序输入,根据循环不变量原则,每次循环要做:
- 从左上到右上
- 从右上到右下
- 从右下到左下
- 从左下到左上
- 画边怎么描述位置?每画一条边都要遵循左闭右开(或者左开右闭原则)?
看讲解后的想法
- 为什么圈数是n/2?
- 因为搜索一圈之后,下一圈的上边会往下走,下一圈的下边会往上走,高度就少2,下一圈的左边会往右走,下一圈的右边会往左走,相当于宽少2了,每次下一圈都会比上一圈的高度宽度都少2,直到这个没有外圈了,没有外圈就是宽度是和高度都是0的时候(偶数的情况),每次宽,高缩小2,直到宽,高是0。因为宽,高都一样,而且是一起缩2的,那么就当高缩2到0的时候就结束了,要缩多少次,就是多少圈。高假设是偶数,偶数-2-2-2一直到0,不就是这个偶数除2吗,就是圈数
- 二维矩阵,startx和starty记录边开始的位置
- int loop = n / 2; loop每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
- int mid= n/2; 记录矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
- int offset=1;每次边长减一
实现过程中的问题
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
看不懂,我玩植物人的。
总结
边界条件众多,还是遵循循环不变量原则,太难了没写出来