您现在的位置是:首页 >技术教程 >代码随想录算法训练营第二天|leetcode 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II网站首页技术教程
代码随想录算法训练营第二天|leetcode 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
leetcode 977.有序数组的平方
想到昨天写的双指针,十分刻意用了一下,感觉还是比较生疏,还得加强练习和思考,然后发现还需要排序,想到了vector的排序sort(),但是觉得直接用不好,也忘记了sort()是类似于快速排序的方法(C++ sort()排序详解),就傻傻地写了一个冒泡排序(比较熟悉),结果果然超时了。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int fast = 0;
int slow = 0;
while (fast < nums.size())
{
nums[slow++] = nums[fast] * nums[fast];
fast++;
}
for (int i = 0; i < nums.size()-1; i++)
{
for (int j = 0; j < nums.size() - i - 1; j++)
{
if (nums[j] > nums[j + 1])
{
int temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
};
双指针法经典题目 | LeetCode:977.有序数组的平方
学习之后,才发现题目给出首先非递减顺序排序的整数数组 nums,说明初始数组的顺序已经排好了,数列递增,但不是单调递增,中间可以有重复。数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
leecode官方在这里有讲解并给出了另一种解法,如果数组 nums 中的所有数都是非负数,那么将每个数平方后,数组仍然保持升序;如果数组 nums 中的所有数都是负数,那么将每个数平方后,数组会保持降序,这样一来,如果我们能够找到数组 nums 中负数与非负数的分界线,那么就可以用类似「归并排序」的方法,这里就不写了(后面再看)。
这里给出代码随想录里的两种解法。
1、暴力排序
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for (int i = 0; i < nums.size(); i++) {
nums[i] *= nums[i];
}
sort(nums.begin(), nums.end()); // 直接使用vector自己的排序算法,快速排序
return nums;
}
};
2、双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> r(nums.size(), 0);//新建一个容器,初始化为0
int k = nums.size() - 1;//r容器的指针指向容器末尾
for (int i = 0, j = nums.size() - 1; i <= j;)
{
if (nums[i] * nums[i] < nums[j] * nums[j])
{
r[k--] = nums[j] * nums[j];
j--;
}
else
{
r[k--] = nums[i] * nums[i];
i++;
}
}
return r;
}
};
这里双指针和昨天不同的是一个指针指向原始容器的首部,一个指针指向原始容器的尾部,然后首尾比较后将大的从尾部存入新的容器,这里还需要定义一个指向新容器的指针指向尾部。
leetcode 209. 长度最小的子数组
这道题第一眼看上去就是想用暴力解法,两个for循环,但又在想怎么使用滑动窗口,没有什么头绪,索性还是得学习一下,感觉十分巧妙。
拿下滑动窗口! | LeetCode 209 长度最小的子数组接描述
滑动窗口
#include<iostream>
#include<vector>
using namespace std;
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT_MAX;
int sum = 0;
int L = 0;
int i = 0;
for (int j = 0; j < nums.size(); j++)
{
sum += nums[j];
while (sum >= target)
{
L = j - i + 1;
result = result < L ? result : L;
sum -= nums[i++];//这里十分巧妙地遍历了子数组中的子数组,不断更改i(子序列的起始位置)
}
}
return result == INT_MAX ? 0 : result;
}
int main()
{
//target = 7, nums = [2,3,1,2,4,3]
vector<int> v;
v.push_back(2);
v.push_back(3);
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(3);
int r = minSubArrayLen(7, v);
if (r == 0)
{
cout << "未找到该条件下的长度最小的子数组" << endl;
}
else
{
cout << "该条件下的长度最小的子数组长度为:" <<r<< endl;
}
return 0;
}
注:引用群里大佬的讲解,INT_MAX 和 INT32_MAX都是C++中常量,分别表示最大整数,定义在头文件limits.h中。因为int占4字节32位,根据二进制编码的规则,INT_MAX = 2^31-1,INT_MIN= -2^31。所以INT_MAX和INT32_MAX的值是相同的。
滑动窗口也可以理解为双指针法的一种,只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动: 如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了,遍历子集的子集)。
窗口的结束位置如何移动: 窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。
从而将O(n^2)暴力解法降为O(n)。
leetcode 59. 螺旋矩阵 II
一入循环深似海 | LeetCode:59.螺旋矩阵II
还是比较复杂,自己没什么头绪,总想找规律但是也没找到(哭笑),还是学习之后跟着敲了一遍。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int startx = 0;
int starty = 0;
int loop = n / 2;//每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int count = 1;
int offset = 1;// 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i, j;
vector<vector<int>> v(n,vector<int>(n,0));
while (loop--)
{
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++)
{
v[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++)
{
v[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--)
{
v[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--)
{
v[i][j] = count++;
}
startx++;
starty++;
offset += 1;
}
int mid = n / 2;
if (n % 2) {
v[mid][mid] = count;
}
return v;
}
};
此题总结:
- 一定要坚持循环不变量原则。
- 每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。