代码随想录
如果不用已知的非递减信息,最简单的实现方法是遍历整个数组求平方,然后用快排进行排序,具体没有尝试过,不知道知否会超时,但能想到的还有更优的解法。
注意题目中的信息,给出的数组是非递减的,求平方之后的数组要求也是非递减的。数组中的数据有正有负,分种情况讨论:
上面的第三种情况更具有普遍性,第一种和第二种可以看作是第三种的特殊情况。在代码实现上把第一种和第二种归入到第三种中进行处理。具体的实现步骤如下:
(1)遍历数组,寻找绝对值最小的数(假设绝对值最小的数为abs_min,该数在数组中的下标为index);
(2)令left = index,right = index + 1(或者left = index - 1, right = index)。在具体实现的时候要注意边界的处理,比如index指向最左边或最右边的情况。
(3)计算。取left指针还是right指针指向的数取决于哪一个指向的数绝对值更小,当left指针指向最左边和right指针指向非法区域的时候停止循环(非法区域是指数组越界)。
代码实现如下:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> res;
int tmp;
/* 查找绝对值最小的数的位置 */
size_t size = nums.size();
int abs_min = abs(nums[0]); //这里必须初始化为第一个数的绝对值
int index = 0;
for(int i=1;i<size;i++)
if(abs(nums[i]) < abs_min)
{
abs_min = abs(nums[i]);
index = i;
}
/* 初始化指针 */
int left = index;
int right = index + 1;
/* 从index往两边遍历 */
while(left >= 0 && right <= size - 1) // 有一个指针越界就退出循环
{
if(abs(nums[left]) < abs(nums[right])) //也可以是 <=
{
tmp = nums[left] * nums[left];
--left;
}
else
{
tmp = nums[right] * nums[right];
++right;
}
res.push_back(tmp);
}
/* left */
while(left >= 0)
{
tmp = nums[left] * nums[left];
--left;
res.push_back(tmp);
}
/* right */
while(right <= size - 1)
{
tmp = nums[right] * nums[right];
++right;
res.push_back(tmp);
}
return res;
}
};
代码随想录中提供了两种方法,暴力求解和双指针法,其实两种方法自己都已经想到了,只是双指针法的实现在代码上可以进一步简化。
代码随想录求解方法
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;
}
};
自己想到的方法要先寻找绝对值最小的数,然后向两边扩散,但其实可以从两边向中间遍历,只不过得到的是从大到小的数据序列,从结果数组的最后向前开始存储就可以了。代码实现如下:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int size = nums.size();
vector<int> res(size,0);
int tmp;
/* 初始化指针 */
int left = 0;
int right = size - 1;
int index = size - 1; //指向存储位置
while(left <= right) //当 left > right的时候退出循环
{
if(abs(nums[left]) > abs(nums[right]))
{
tmp = nums[left] * nums[left];
left++;
}
else
{
tmp = nums[right] * nums[right];
right--;
}
res[index] = tmp;
index--;
}
return res;
}
};
注意边界的处理,循环条件的设定与条件变量的初始化以及循环体内的变量的更新位置与数据处理的先后位置有关,怎么理解第二个点呢?简单说就是使得每个条件变量对应的数据都要被处理一次。例如上面的双指针法中,left = 0和 right = size - 1,这没有什么问题,关键是while循环条件中包不包含等号的问题,在这个问题中循环体内先处理数据(即求平方),再改变条件变量的值,因此应该包含等号,当left == right的时候还剩最后一个数据,需要再执行一次循环体。
这个题之前已经做过了,还有些印象,第一感觉是双指针,但是今天的作业提醒中已经明确指出应该用滑动窗口,于是就有个疑问:滑动窗口和双指针有什么区别?搜了一下,这里搬运过来:
滑动窗口与双指针的区别
双指针
滑动窗口
简单理解,双指针只是两个指针,而滑动窗口是两个指针组成的一个窗口。
同样需要两个指针,当窗口内的和sun < target时,移动右指针right,直到满足sum >= target,然后移动左指针left来缩小窗口,直到不满足条件,注意每种满足条件的情况都要和最小窗口长度作比较,即满足条件时要更新窗口的最小长度。当right移动到最右边且不满足sum >= target时结束移动。代码如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0, right = 0; //left指向窗口左端点,right指向窗口右端点的下一个点
int size = nums.size();
int sum = 0,minLen = INT_MAX;
//当right == size - 1 且 sun < target的时候退出循环
while(right < size)
{
//right
while(sum < target && right < size) sum += nums[right++];
//更新窗口长度
if(sum >= target)
minLen = minLen > (right - left) ? (right - left) : minLen;
//left
while(sum >= target && left < size)
{
sum -= nums[left++];
//更新窗口长度
if(sum >= target)
minLen = minLen > (right - left) ? (right - left) : minLen;
}
}
return minLen == INT_MAX ? 0 : minLen;
}
};
两层循环,外层循环变量i作为左端点,内层循环变量j作为右端点。代码实现如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int size = nums.size();
int sum;
int minLen = INT_MAX;
for(int i=0;i<size;i++)
{
sum = 0;
for(int j=i;j<size;j++)
{
sum += nums[j];
if(sum >= target)
minLen = minLen > (j-i+1) ? (j - i + 1) : minLen;
}
}
return minLen == INT_MAX ? 0 : minLen;
}
};
代码随想录实现
代码随想录中提供了两种方法,暴力求解和滑动窗口,其实这两种方法都已经实现,但是滑动窗口求解的代码还可以简化,在看了代码随想录给出的代码之后,自己再重新实现一遍。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int minLen = INT_MAX;
int sum = 0,i = 0, j = 0;
int size = nums.size();
for(j = 0; j < size; j++)
{
sum += nums[j];
while(sum >= target)
{
int len = j - i + 1;
minLen = len < minLen ? len : minLen;
sum -= nums[i++];
}
}
return minLen == INT_MAX ? 0 : minLen;
}
};
相比于自己写的代码,这种思路更清晰,更简洁。
滑动窗口要注意窗口的左右端点以及窗口大小的确定,以及什么情况下改变左端点,什么情况下改变右端点。
这个题之前没有做过,看了题之后没有什么思路,想着用代码模拟旋转过程,但是不知道怎么实现。直接抄一遍代码随想录给出的代码:
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;
//下面开始的4个for循环就是模拟转了一圈
for(j = starty; j < starty + n - offset; j++)
res[startx][j] = count++;
for(i = startx; i < startx + n - offset; i++)
res[i][j] = count++;
for(;j > starty; j--)
res[i][j] = count++;
for(;i > startx; i--)
res[i][j] = count++;
//第二圈开始的时候,起始位置各自要加1
startx++;
starty++;
offset += 2;
}
//如果n为奇数,则需要单独给矩阵最中间位置赋值
if(n % 2)
res[mid][mid] = count;
return res;
}
};
以一圈作为一个大循环,一行或一列作为一个小循环,每个大循环的起点为从(0,0)开始,往右下角偏移。
前面两个题之前做过,现在还能做出来,但是第三题没做过就不会了,还得继续努力啊。