977. 有序数组的平方
最暴力的:平方之后,再sort一下,O(n+nLog(n))不符合要求。
期待一个O(n)解法,一开始想了十分钟一直想着原地升序,我的思路1:首先平方,变成一个降序(S1部分)又升序(S2部分)的数组,对每个左边S1递减的部分,确保其每一个元素后面的所有比它大,S2不用管,但明显思路错误。
思路2: S1先升序,之后L指针1从S1打头,R指针2从S2打头,凡是L指向的大于R就交换,L++,R++。毫无理论依据,乱想的。事实证明错误!
看了视频之后,可以开一个新数组!!!顿悟!那就很简单了
class Solution {
public:
vector sortedSquares(vector& nums)
{
vector out;
int l = 0;
int r = nums.size() - 1;
while(l <= r)
{
if(abs(nums[l]) > abs(nums[r]))
{
out.push_back(nums[l]*nums[l]);
l++;
}
else
{
out.push_back(nums[r]*nums[r]);
r--;
}
}
l = 0;
r = nums.size() - 1;
while(l < r)
{
int tmp = out[l];
out[l] = out[r];
out[r] = tmp;
l++;
r--;
}
return out;
}
};
209. 长度最小的子数组
我的一开始写法:前缀和+暴力遍历窗口大小(从1...到nums.size()),过了测试样例,但TLE了,时间复杂度是O(n^2)。
class Solution {
public:
int minSubArrayLen(int target, vector& nums)
{
// 来一个前缀和
vector sum(nums.size()+1);
sum[0] = 0;
for(int i = 1; i <= nums.size();i++)
sum[i] = sum[i - 1] + nums[i - 1];
// 开始处理
for(int len = 0; len < nums.size(); len++) //遍历不同的长度
{
for(int l = 1; l + len <= nums.size();l++ ) // 一个len 遍历这个nums
{
cout << len << ' ' << l << endl;
int now_sum = sum[l+len] - sum[l-1];
if(now_sum >= target)
return len + 1;
}
}
return 0;
}
};
看了视频,学习了同向双指针的另一种用法——滑动窗口,NB!(虽然好像以前见过,又忘记了)根据两个指针i、j表示这个窗口的起始和结束位置,重点是最外层的 j 表示窗口的结束位置。时间复杂度是O(n)的。
class Solution {
public:
int minSubArrayLen(int target, vector& nums)
{
int i = 0; //i表示这个滑动窗口的起始位置
int sum = 0;
int uu = INT32_MAX;
for(int j = 0; j < nums.size();j++) //j表示这个滑动窗口的结束位置
{
sum += nums[j];
while(sum >= target) //左缩窗口
{
uu = min(uu,j - i + 1);
sum -= nums[i++];
}
}
if(uu != INT32_MAX)return uu;
else return 0;
}
};
904. 水果成篮 【中等】
呜呜呜,每一个变量的定义一定要想清楚,不然乱试尊的会死,such as 本题的 int left。我的写法: 这个left怎么调都过不了......痛苦,看了题解 是要用哈希表代替 left 这一块的作用?
class Solution {
public:
int totalFruit(vector& fruits)
{
// 还是双指针-滑动窗口的思想
// j表示窗口结束位置 i表示窗口开始位置 维护[i,j]是永远满足条件的一个区间
// left 表示对于结束位置 比如是5 前一个不等于5 的第一个下标 比如 3335 left 期待是0
int result = 1;
int i = 0;
vector now;
now.push_back(fruits[0]);
int left = 0;
for(int j = 1; j < fruits.size();j++)
{
auto it = find(now.begin(),now.end(),fruits[j]);
if(it == now.end()) //没找到
{
if(now.size() == 2)
{
// cout << i << " " << j << endl;
result = max(result,j - i );
i = left;
int tmp = now[0];
now[0] = now[1];
now[1] = tmp;
now.pop_back();
now.push_back(fruits[j]);
// left = j;
cout << "1 " << result << endl;
}
else
{
now.push_back(fruits[j]);
result = max(result,j - i + 1 );
cout << "2 " << result << endl;
// left = j;
}
}
else //找到了
{
result = max(result,j - i + 1 );
cout << fruits[j] <<"3 " << result << ' ';
cout << i << " " << j << endl;
}
if(fruits[j] != fruits[left])left = j;
}
return result;
}
};
todo
76. 最小覆盖子串 【困难】
todo
59. 螺旋矩阵 II
下面是我按照初步思路写就AC的代码,思路很简单,确保四个方向依次走到头,头的标准是:以右边走到头为例子就是(右边是边界 || 右边有数了)。按着这个逻辑写完,才发现这个写法正好没有让x,y出out的边界,而且产生了任一个方向左闭右开的效果,和正解不谋而合,并且比carl写的最外层while循环简单,他写的还要算出loop次数+中心额外处理。
class Solution {
public:
vector> generateMatrix(int n)
{
// 初步思路————向 右 下 左 上 四个方向依次走到尽头 直到无路可走?
vector> out(n,vector(n,0)); //二维数组的初始化大小
int x = 0; //当前坐标
int y = 0;
int k = 1;
// 当四周都填了(数字或者无路)
while((out[x][y] == 0) || ( !(x == 0 || out[x - 1][y] != 0) || !(x == n-1 || out[x+1][y] != 0) || !(y == 0 || out[x][y-1] != 0) || !(y == n-1 || out[x][y+1] != 0)))
{
// 往右边走
while( !(y == n-1 || out[x][y+1] != 0) && out[x][y] == 0 )
{
out[x][y] = k++;
y++;
}
// 往下走
while( !(x == n-1 || out[x+1][y] != 0) && out[x][y] == 0 )
{
out[x][y] = k++;
x++;
}
// 往左边走
while(!(y == 0 || out[x][y - 1] != 0)&& out[x][y] == 0 )
{
out[x][y] = k++;
y--;
}
// 往上走
while(!(x == 0 || out[x - 1][y] != 0)&& out[x][y] == 0 )
{
out[x][y] = k++;
x--;
}
if(out[x][y] == 0 && ( (x == 0 || out[x - 1][y] != 0) && (x == n-1 || out[x+1][y] != 0) && (y == 0 || out[x][y-1] != 0) && (y == n-1 || out[x][y+1] != 0)))
{
out[x][y] = k;
}
}
// cout << '[';
// for(int i = 0; i < n; i++)
// {
// cout << '[';
// for(int j = 0; j < n; j++)
// if(j != n-1) cout << out[i][j] << ',';
// else cout << out[i][j];
// cout << "],";
// }
// cout << ']';
return out;
}
};
题型:
【1】二分
【2】双指针(同向和双向)
【3】滑动窗口
【4】模拟,考察代码实现能力
还有1道半的滑动窗口拓展
还有2题螺旋矩阵类似的模拟题
滑动窗口的拓展-水果成篮调的很辛苦,其他还可以。大概总共用时4小时