题目描述
输入一个非负整数 S,打印出所有和为 S 的连续正数序列(至少含有两个数)。
例如输入 15,由于 1+2+3+4+5=4+5+6=7+8=15,所以结果打印出 3 个连续序列 1∼5、4∼6 和 7∼8。
数据范围
0≤S≤1000
样例
输入:15 输出:[[1,2,3,4,5],[4,5,6],[7,8]]
方法一:双指针 O(sum)
这道题可以用双指针的思想来做,具体步骤如下:
1.设置两个指针 i 和 j ,分别往后进行遍历,而 s 记录的是从 i 到 j 的和。
2.只要 s 是小于 sum 的就将 j 往后加 1 并加上它。
3.如果 s 等于 sum ,则将 i 到 j 的所有数加入数组当中。
4.j 不用往前移,只用将 s 减去 i 即可。这是因为将 s 减去 i 后是一定比 sum 要小的,这就类似于一个滑动窗口,不断的调整首尾两个边界。
我们来看个例子,就拿题目样例举例,假设给定的 sum
为 15
:
第一轮:j
一直往后移动,直至 i
和 j
之间元素之和大于等于 15
。
此时 s
为 15
,故 [1,2,3,4,5]
是一组答案,然后使 i
往后移一位,减小 sum
的值。
第二轮:j
继续往后移,直至 i
和 j
之间元素之和大于等于 15
。
发现已经超过 15
,故将 i
往后移一位。
第三轮: 因为上一轮 i
移动后 s
已经等于 15
,故 j
无需后移,而 [4,5,6]
是一组答案,然后将 i
继续往后一位。
第四轮:j
继续后移,直至 i
和 j
之间元素之和大于等于 15
。
此时 s
已经大于 15
,故直接将 i
后移一位。
第五轮:j
继续后移,直至 i
和 j
之间元素之和大于等于 15
。
此时 s
等于 15
,故 [6,7,8]
也是一组答案,然后再将 i
继续后移一位。
第六轮:j
继续后移,直至 i
和 j
之间元素之和大于等于 15
。
sum
已经大于 15
,故 i
继续往后移一位。
此时 i 已经大于 sum/2 ,故直接退出返回最终答案。
这里之所以以 i <= sum/2 为结束标志,是因为连续序列的答案中下边界最大也不可能超过 sum/2 。比如 15 的 sum/2 等于 7 ,故有一个连续序列 [7,8] 的答案,但不会出现 [8,8] 的情况,并且 [8,9] 已经大于 sum 了。再比如 16 的 sum/2 等于 8 ,而 [8,9] 已经大于 16 了,下边界再增加也没有用。
class Solution { public: vector<vector<int> > findContinuousSequence(int sum) { vector<vector<int>> ans; for (int i = 1, j = 1, s = 1; i <= sum / 2; i++) { while (s < sum) j++, s += j; if (s == sum && j > i) { vector<int> temp; for (int k = i; k <= j; k++) temp.push_back(k); ans.push_back(temp); } s -= i; } return ans; } };
欢迎大家在评论区交流~