求和为s的连续正数序列.
如,输入9,输出序列:{2,3,4}、和{4,5}
下面是暴力循环法:
// 暴力法
vector<vector<int>> FindContinuousSequence(int target) {
int sum = 0;
vector<vector<int>> result;
for (int i = 1; i <= target / 2; ++i)
{
for (int j = i; j < target; ++j)
{
sum += j;
if (sum == target)
{
vector<int> temp;
for (int k = i; k <= j; ++k)
{
temp.push_back(k);
}
result.push_back(temp);
break;
}
else if (sum > target)
break;
}
sum = 0;
}
return result;
}
下面是双指针法,也就是滑动窗口法:时间复杂度O(n)
滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)。
要用滑动窗口解这道题,我们要回答两个问题:
第一个问题,窗口何时扩大,何时缩小?
第二个问题,滑动窗口能找到全部的解吗?
对于第一个问题,回答非常简单:
当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动
当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动
当窗口的和恰好等于 target 的时候,我们需要记录此时的结果。设此时的窗口为 [i, j)[i,j),那么我们已经找到了一个 ii 开头的序列,也是唯一一个 ii 开头的序列,接下来需要找 i+1i+1 开头的序列,所以窗口的左边界要向右移动
// 双指针法
vector<vector<int>> findContinuousSequence(int target) {
int i = 1; // 右指针
int j = 1; // 左指针
int sum = 0;
vector<vector<int>> result;
while (i <= target / 2) // 考虑边界,i可能为target/2,如9/2=4
{
if (sum < target)
{
sum += j++;
}
else if (sum > target)
{
sum -= i++;
}
else
{
vector<int> temp;
// 能进到这里,上一次肯定是j++了,所以k<j为结束条件
for (int k = i; k < j; ++k)
{
temp.push_back(k);
}
result.push_back(temp);
sum -= i++; // 注意要右移i
}
}
return result;
}