题目地址:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/
题目描述
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
题目示例
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
解题思路
暴力法:分析题目可得,若要求序列长度至少大于 2 ,则枚举上界为(target-1)/2(等价于target/2向下取整)。然后,从1开始枚举每个正整数,判断以它为起点的序列和 sum 是否等于target,若sum>target,则枚举的起点不符合要求,初始化sum=0,并跳出此次循环,并将枚举的起点i自增1,进行下一轮枚举。若sum==target,则将此轮枚举结果插入到res中,最后保存到集合v中,并初始化sum=0,结束此轮枚举。
等差数列之和+双指针:利用等差数列求和公式可得,数列之和=(首项+尾项)*项数 / 2,即target=(start+end)*(end-start+1)/2。
在本题中,我们使用双指针left和right表示当前枚举到的以left为起点到right的区间,用sum表示区间[left,right]的区间之和,然后使用求和公式sum = (left+right)*(right-left+1)/2与target相比较,,根据题目要求序列长度至少为2,初始可令left=1,right=2。
- 若sum>target,则说明以left为起点,不存在right使得sum==target,此时,枚举下一个起点,指针left右移;
- 若sum<targe,则说明以left为起点,right为尾点的区间之和sum还不够大,也就是right还可以更大,此时,指针right右移;
- 若sum==target,则说明找到了满足条件的解[left,right],将解的序列放入数组v中,然后枚举下一个起点,即指针left右移;
通过对暴力法和双指针方法对比发现,双指针考虑到了如果已知 [left,right]的区间和sum等于target ,那么枚举下一个起点的时候,区间 [left+1,right]的和必然小于target ,此时,不再需要从left+1开始重复枚举,而是从right+1开始枚举
程序源码
方法1——暴力法
class Solution { public: vector<vector<int>> findContinuousSequence(int target) { vector<vector<int>> v; vector<int> res; int sum = 0, upLimit = (target - 1) / 2; for(int i = 1; i <= upLimit; i++) { for(int j = i; ; j++) { sum += j; if(sum > target) { sum = 0; break; } else if(sum == target) { res.clear(); for(int k = i; k <= j; k++) { res.emplace_back(k); } v.emplace_back(res); sum = 0; break; } else { continue; } } } return v; } };
方法2——双指针
class Solution { public: vector<vector<int>> findContinuousSequence(int target) { vector<vector<int>> v; vector<int> res; int left = 1 , right = 2; while(left < right) { int sum = (left + right) * (right - left + 1) / 2; if(sum > target) { left++; } else if(sum == target) { res.clear(); for(int i = left; i <= right; i++) { res.emplace_back(i); } v.emplace_back(res); left++; } else { right++; } } return v; } };