题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
解题思路
方法一:穷举
1、思路:穷举所有以1到sum/2为起点的连续正数序列,内层循环最多只需要累加到。
2、代码:
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > result;
vector<int> singret;
int j, count = 0;
for (int i = 1; i <= sum / 2; i++) {
j = i;
while (count < sum) {
singret.push_back(j);
count += j++;
}
if (count == sum)
result.push_back(singret);
count = 0;
singret.clear();
}
return result;
}
};
3、复杂度:
时间复杂度:O(),
空间复杂度:O()。
方法二:数学优化
1、思路:以x为起点,以某个y为终点的连续序列的和S可以通过等差数列前n项和公式直接得到,即,变形后可得,因此可以直接求得y,当y为整数时合法。
2、代码:
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > result;
vector<int> singret;
double y = 0;
double delta = 0;
for (int x = 1; x <= sum / 2; x++) {
delta = 1 + 4 * x * x - 4 * x + 8 * sum;
if (delta < 0) continue;
y = (-1 + sqrt(delta)) / 2;
if (y == floor(y)) {
singret.clear();
for (int k = x; k <= y; k++) {
singret.push_back(k);
}
result.push_back(singret);
}
}
return result;
}
};
3、复杂度:
时间复杂度:O(n);
空间复杂度:O()。
方法三:双指针
1、思路:使用两个指针分别指向连续正数序列的左右边界,
- 当区间内的序列和小于S时,右边界右移,扩大区间,调整序列和;
- 当区间内的序列和大于S时,左边界右移,收缩区间,调整序列和;
- 当区间内的序列和等于S时,记录序列,左右边界均右移,然后重复上述操作。
2、代码:
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > result;
vector<int> singret;
int i, left = 1, right = 1, count = 0;
while (left <= sum / 2) {
if (count < sum) {
count += right;
right++;
} else if (count > sum) {
count -= left;
left++;
} else {
singret.clear();
for (i = left; i < right; i++) {
singret.push_back(i);
}
result.push_back(singret);
count -= left;
left++;
count += right;
right++;
}
}
return result;
}
};
3、复杂度:
时间复杂度:O(n);
空间复杂度:O()。