LeetCode977.有序数组的平方
题目描述:给你一个按 非递减顺序 排序的整数数组 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]
解题思路
·看到题目的第一个想法是使用暴力排序法解决,将每个数据平方后,再进行排序
·这道题比较难想到使用双指针进行求解,笔者也是观看了视频之后才意识到可以使用双指针法进行求解
·观察数组发现,其实数组是有序的,而且负数的平方后也是正数,所以顺序需要进行调整。定义指针i指向起始位置,指针j指向终止位置。定义一个数组result,并且与nums数组大小一致,并且定义变量k使其指向result数组终止位置。
1.暴力求解法
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;
}
};
2.双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int i = 0,j = nums.size()-1;//定义指针i指向数组起始位置,指针j指向数组终止位置
int k = nums.size()-1;//定义变量k指向数组终止位置
vector<int> result;//定义数组result
result.resize(nums.size());//初始化数组result并且大小与nums数组大小一致
while(i <= j){
if(nums[i]*nums[i] > nums[j]*nums[j]){
result[k] = nums[i]*nums[i];//若nums[i]^2大于nums[j]^2则将nums[i]^2值放入k中
k--;//k向前移动
i++;//ii向后移动
}else{//同上
result[k] = nums[j]*nums[j];
k--;
j--;
}
}
return result;
}
};
难点/易错点
·使用vector定义数组后,一定要初始化数组,否则无法正常使用数组
·while循环中一定是i<=j因为如果没有等于,会出现少比较一个数的情况
总结
笔者过往在遇到这一类问题的第一直觉是使用暴力求解,因为可以直接暴力破解。在未看解法前,从未思考过使用双指针法进行求解。通过书写这道题,加深了对双指针的印象,为解题增加了一个新的思路。
LeetCode209.长度最小的子数组
题目描述:
给定一个含有 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
解题思路
·看到题目的第一个念头是使用分治算法进行求解,但是思考后想起来,分治法求解的是最长子序列,并非求解长度最小的子数组
·依旧是使用暴力求解,使用两个for循环,将所有可能的子序列进行寻找,这样虽然可以写出来,但是时间复杂度是O(n^2),但是题目要求的时间复杂度是O (nlogn),所以暴力求解方法也不行
·在看了题解后最终决定使用滑动窗口进行求解
因为暴力求解不能通过,所以就不进行书写了
滑动窗口法
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;//将result定义成最大值,因为后面需要有min操作
int i = 0,j = 0;//将初始指针与终止指针先都定义在起点
int subL = 0;//最短子序列
int sum = 0;//满足条件的集合
for(j = 0;j < nums.size();j++){
sum += nums[j];
while(sum >= target){//如果集合中的数大于等于目标数
subL = j-i+1;//最短子序列长度
result = min(result,subL);//取最短序列
//数组向右移动
sum = sum - nums[i];
i++;
}
}
return result==INT32_MAX?0:result;//如果result的值等于INT32_MAX,说明灭有找到满足条件的子数组,此时返回result的值
}
};
难点
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置。再暴力求解法中,一个for循环为起始位置,一个for循环为终止位置,用两个for循环完成了一个不断搜索区间的过程。滑动窗口只用一个for循环来表示终止位置。
实现滑动窗口主要需要确定以下三点:
·窗口内是什么
·如何移动起始位置
·如何移动结束位置
窗口就是满足窗口内和大于等于目标值的长度最小的连续子数组
窗口的起始位置如何移动:如果当前窗口的值大于目标值,窗口就需要向前移动(也就是缩小)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
总结
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而降低时间复杂度。本人从未用过滑动窗口的解法(可能用了也不知道),滑动窗口的代码较简单,但是理解过程比较难,我也是看了三遍题解才懂,学习了一种全新的解题方法。
LeetCode59.螺旋矩阵II
题目描述:
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1 输出:[[1]]
解题思路:
·看到这道题,第一件事情是要确定好边界值,千万不能使用每条边都遍历一遍,再把重复元素删除,这是吃力不讨好的方法,应该使用左闭右开进行遍历。
·就以示例为例,第一次遍历12,第二次遍历34,第三次遍历56,第四次遍历78,这样可以做到不重不漏,如果遇到奇数个数,那个中间的数进行单独操作
以下是代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n,vector<int>(n,0));
int half = n/2;//矩阵的中间位置
int offset = 0;//定义初始位置
int value = 1;//对矩阵内元素进行赋值
for(int k = 1;k <= half;k++){
for(int j = offset;j < n-1-offset;j++)
res[offset][j] = value++;//上行从左到右(左闭右开)
for(int i = offset;i < n-1-offset;i++)
res[i][n-1-offset] = value++;//右列从上到下(左闭右开)
for(int j = n-1-offset;j > offset;j--)
res[n-1-offset][j] = value++;//下行从右到左(左闭右开)
for(int i = n-1-offset;i > offset;i--)
res[i][offset] = value++;//左列从下到上(左闭右开)
offset++;
}
if(n%2 == 1)//若为奇数,则单独给矩阵中间赋值
res[half][half] = n*n;
return res;
}
};
总结
加深了对循环不变量原则的印象,每一步循环的边界值一定要判断清楚,判断准确,这样循环才能做到不重不漏,正确的遍历。