剑指Offer JZ41 和为S的连续正数序列 C++实现

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

解题思路


方法一:穷举

1、思路:穷举所有以1到sum/2为起点的连续正数序列,内层循环最多只需要累加到\sqrt{sum}

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(n\sqrt{n}),

空间复杂度:O(\sqrt{n})。


方法二:数学优化 

1、思路:以x为起点,以某个y为终点的连续序列的和S可以通过等差数列前n项和公式直接得到,即S = (x + y)(y - x + 1)/2,变形后可得y^2 + y - x^2 + x - 2S = 0,因此可以直接求得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(\sqrt{n})。


方法三:双指针

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(\sqrt{n})。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值