文档讲解:代码随想录(programmercarl.com)
视频讲解:
1. 双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili
2. 《代码随想录》算法视频公开课 (opens new window):拿下滑动窗口! | LeetCode 209 长度最小的子数组
状态:能坚持但不多5555
LeeCode 977.有序数组的平方
题目链接:977. 有序数组的平方 - 力扣(LeetCode)
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
思路:看到题目的第一反应是需要两步操作:数值平方+排序。数值平方通过for循环遍历就可以,然后在循环里面选择一个排序算法进行排序,正常输出数组就行。
那么首先就是暴力解法
//暴力解法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int size= nums.size();
for(int i=0;i<size;i++){
nums[i] *= nums[i];
}
sort(nums.begin(), nums.end()); // 快速排序
return nums;
}
};
注意:此时的时间复杂度为O(n+nlogn),其中快速排序的时间复杂度为O(nlogn)
其中排序运算只需要一行代码就可以搞定,但是这样的话排序算法内部的运行原理我们一知半解,也不能很好的锻炼我们的写代码的能力,所以我们换思路用双指针法解决上述题目
双指针法:这个方法在上一篇文章中提到过,用过移除数组中等于目标值的元素,当时是快慢指针同向移动,快指针寻找新数组(不含目标值),慢指针更新新数组的下标位置,这样可以优化时间复杂度,相比于两个for循环所需时间复杂度为O(N^2)来说,该方法只需O(n).
说回本题目,基于给定的数组的特殊性,非递减数组中含有负数和整数,那平方运算之后按照从大到小的排序,它的走向一定是往中间靠拢的,且最大值要么在最右边或者最左边,那么就可以考虑双指针,异向双指针,指针移动的同时进行数值比较。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int k= nums.size()-1;
vector<int> result(nums.size(), 0);
// vector<int> a(10,1); //定义了10个整型元素的向量,且给出每个元素的初值为1
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;
}
};
LeeCode 209.长度最小的子数组
题目链接:力扣题目链接
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
- 输入:s = 7, nums = [2,3,1,2,4,3]
- 输出:2
- 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
解题思路:
首先是暴力解法:通过两个for循环:外循环遍历,内循环寻找最优子序列注意:时间复杂度O(N^2) 空间复杂度O(1)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum;//子序列的和
int subLength=0;//子序列的长度
int result=INT32_MAX; //最终结果
//INT_MAX = 231 -1 = 2147483647
//需要将某一个变量的初值赋值为最大的整数或者最小的整数,让程序正常运行,防止越界
//应该跟int a=0;是一样的概念
for(int i=0;i<nums.size();i++){ //子序列的起点i
sum=0;
for(int j=i;j<nums.size();j++){ //子序列的终止点j
sum += nums[j];
if(sum>=target){
subLength = j-i+1;
result = result < subLength ? result:subLength;
break; //
}
}
}
return result == INT32_MAX ? 0:result;//
}
};
//s = 7, nums = [2,3,1,2,4,3]
// i=0 sum=0 j=i=0 sum=0+nums[j]=2 if不满足
// j++=1 sum=nums[0]+nums[1]=5 if不满足
// j++=2 sum=nums[0]+nums[1]+nums[2]=6 if不满足
// j++=3 sum=nums[0]+nums[1]+nums[2]+nums[3]=8 if满足 sublength=j-i+1=3-0+1=4
// result=sublength=4 break 结束第一次内循环
// i=1 sum=0(重新归0) j=i=1 sum=0+nums[1]=3 if不满足
// j++=2 sum=nums[1]+nums[2]=4 if不满足
// j++=3 sum=nums[1]+nums[2]+nums[3]=6 if不满足
// j++=4 sum=nums[1]+nums[2]+nums[3]+nums[4]=10 if满足 sublength=j-i+1=4-1+1=4
// result=sublength=4 break 结束第二次内循环
// i=2 sum=0(重新归0) j=i=2 sum=0+nums[2]=1 if不满足
// j++=3 sum=nums[2]+nums[3]=3 if不满足
// j++=4 sum=nums[2]+nums[3]+nums[4]=7 if满足 sublength=j-i+1=4-2+1=3
// result=sublength=3 break 结束第三次内循环
// i=3 sum=0(重新归0) j=i=3 sum=0+nums[3]=2 if不满足
// j++=4 sum=nums[3]+nums[4]=6 if不满足
// j++=5 sum=nums[3]+nums[4]+nums[5]=9 if满足 sublength=j-i+1=5-3+1=3
// result=sublength=3 break 结束第四次内循环
// i=4 sum=0(重新归0) j=i=4 sum=0+nums[4]=4 if不满足
// j++=5 sum=nums[4]+nums[5]=7 if满足 sublength=j-i+1=5-4+1=2
// result=sublength=2 break 结束第四次内循环
// i=5 sum=0(重新归0) j=i=5 sum=0+nums[5]=3 if不满足
// j++=6 for循环不满足 i++=6 结束外循环
// 此时result=2 返回
个人疑惑1:看到题目第一想法是从第一个元素开始相加直到其和>=target,之后从下一个元素开始继续相加直到其和>=target,..., 直到数组遍历完。再将这些和进行比较,得到长度最小的数组返回其长度。理解错误,没有理解两个for循环的正确用法,菜鸡胡思乱想
个人疑惑2:两个for循环中通过i作为子序列的起点,j作为子序列终止点, 与双指针法的用法相似,所以不知道能不能通过双指针法解决?滑动窗口也可以理解为双指针的一种。
接下来看一下通过滑动窗口解决这个题目:
滑动窗口:不断调节子序列的初始位置和终止位置,从而得到理想结果
(因为今天有点忙,所以暂时更新这些)-------2023.08.24 (六级过了哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈!!!!!)
继续更新---------2023.08.25
滑动窗口需要确认的三点:
1. 窗口内是什么?满足元素加和>=s的最小长度连续子序列
2. 窗口起始位置怎么移动?满足元素加和>=s 窗口移动一位
3. 窗口结束位置怎么移动?遍历数组的指针
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum=0;//子序列的和
int subLength=0;//子序列的长度
int result=INT32_MAX; //最终结果
int i=0; //i为滑动窗口的起始位置
for(int j=0;j<nums.size();j++){ //j为滑动窗口的结束位置,对数组进行遍历
sum += nums[j];
while(sum >= target){ //一旦满足while条件,更新i
subLength = (j-i+1);
result = result < subLength ? result : subLength;
sum -= nums[i++]; //在赋值完result之后,减去子序列的下标为i的元素,完成移动滑动窗口起始位置
}
}
return result == INT32_MAX ? 0: result;
}
};
此时的时间复杂度为O(n)
LeeCode 59. 螺旋矩阵II
题目链接:力扣题目链接
给定一个正整数,生成一个包含1到n^2所有元素,且元素按顺时针螺旋排列的正方形矩阵。示例:
输入:3
输出:[1,2,3],[8,9,4],[7,6,5]
n=3时
n=4时
类似地,n=5时,循环2圈,剩余元素25,n=6时,循环3圈,依次类推n^2个元素时,循环n/2圈(n为奇数时在循环结束时将遍历的最后一位元素赋值给它)。
代码如下:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0));
int startx = 0, starty = 0;
int loop = n / 2;
int mid = n / 2;
int count = 1;
int offset = 1;
int i,j;
while (loop --) {
i = startx;
j = starty;
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++;
}
// 第二圈
startx++;
starty++;
offset += 1;
}
// n为奇数
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
小总结:
1. 代码随想录的刷题速度很慢,因为代码方面的基础很差,基本上一道题目从看到到复现代码往往需要3个小时以上,而且因为自己之前是学习C#,对于C++的代码还需要学习和查资料,所以时间会更久,很担心这样的进度会很影响自己坚持的决心和秋招的进度。
2. 因为在准备秋招的同时也在完成手头上的论文,有点两边都顾不上的感觉,所以刷题的专注度不够高,效率也不高。
3. 但两天的刷题也发现C++在代码实现上会更简洁更方便,所以很不理智的选择以C++为主语言来准备笔试和面试(反正C#方面好像也没有什么经历和项目可以讲)。
(快速排序算法跟其他的排序算法之后一起总结吧)。