2021/2/5二刷
57-II的sum可以动态改变,没有必要调用多次等差数列公式
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
if(target < 3) return {{}};
vector<vector<int>> ans;
int left = 1, right = 2;
int sum = 3;
while(left < right) {
if(sum == target) {
ans.push_back(re(left,right));
}
if(sum < target) {
right++;
sum += right;
} else {
sum -= left;
left++;
}
}
return ans;
}
vector<int> re(int left, int right) {
vector<int> tmp;
for(int i = left; i <= right; i++) {
tmp.push_back(i);
}
return tmp;
}
};
需要注意一下边界的问题
这样写会把自己也包括进去
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
if(target < 3) return {{}};
vector<vector<int>> ans;
int left = 1, right = 2;
int sum = left;
while(left < right) {
if(sum == target) {
ans.push_back(re(left,right));
}
if(sum < target) {
sum += right;
right++;
} else {
sum -= left;
left++;
}
}
return ans;
}
vector<int> re(int left, int right) {
vector<int> tmp;
for(int i = left; i < right; i++) {
tmp.push_back(i);
}
return tmp;
}
};
57-I
题目描述
前面做过两题一摸一样的,思路略
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i = 0, j = nums.size() - 1;
while(i < j) {
if(nums[i] + nums[j] > target) j --;
else if(nums[i] + nums[j] < target) i ++;
else return vector<int>{nums[i], nums[j]};
}
return {};
}
};
57-II
思路 双指针法/滑动窗口法
先记录一下自己的错误想法,思路大概是先让窗口包括可能取到的最大连续序列,然后不断缩小窗口,每次缩小左或者右通过判断子序列和和target之差来决定,代码会导致死循环,并且找到最长解之后就找不到第二组解
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
int i = 1, j = target;
vector<vector<int>> anss;
while(i < j) {
if(sum(i,j) < target) {
if(target - sum(i, j) >= i && target - sum(i, j) < j) i --;
else if(target - sum(i, j) >= j) j ++;
}
else if(sum(i, j) > target) {
if(sum(i , j) - target >= j) j --;
else if(sum(i, j) - target < j && sum(i, j) - target >= i) i ++;
}
else {
anss.push_back(re(i, j));
i++;
}
}
return anss;
}
int sum(int i, int j) {
return (i + j) * (j - i + 1) / 2;
}
vector<int> re(int i, int j) {
vector<int> ans;
for(int k = i; k <= j; k ++) ans.push_back(k);
return ans;
}
};
总之是非常蠢的代码
但是只要稍微改一下思路就正确了,让指针 L R都从右开始 L = 1 R = 2,构成一个左闭右闭区间[1,2],
然后窗口只要一直往右移动就行了。
- sum < target 说明窗口内元素总合不够target大小,移动右指针R扩大窗口
- sum > target 说明窗口内元素总和过大,移动左指针L缩小窗口
- sum = target 返回一组解。
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
//初始[1,2]左闭右闭区间
int l = 1;int r = 2;
vector<vector<int>> anss;
while(l < r) {
int res = sum(l, r);
if(res == target) {
anss.push_back(re(l, r));
l ++;
}
else if(res < target) r++;
else l++;
}
return anss;
}
int sum(int i, int j) {
return (i + j) * (j - i + 1) / 2;
}
vector<int> re(int i, int j) {
vector<int> ans;
for(int k = i; k <= j; k ++) ans.push_back(k);
return ans;
}
};
这里要注意的是,
- 关于得到结果时
if(res == target) {
anss.push_back(re(l, r));
l ++;
}
在得到答案之后,l一定要右移,否则又会死循环。为什么是l右移呢,因为以l为开头的解有且只有一个。
2. 关于终止条件
while(l < r)
当l == r时,r一定处于 target/2 + 1的位置 ,这时候继续往右已经没有意义。置于为什么r会处在这个位置分析如下:
- r不可能处在target/2 + 2, 因为target/2 + 1的最小窗口即[target/2, target/2+1]的合一定大于等于target,所以r不会再右移
- r一定会移动target/2 + 1, 因为最接近target且一定小于target的是[target/2 -1, target/2]而通过滑动窗口的正确性我们能知道区间一定会探索这个位置,所以r将会变成target/2 + 1。
时间复杂度O(target)
空间复杂度O(1)