和为S的连续正数序列

题目描述

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

解法一:

暴力遍历,遍历数组,针对遍历到的每个数字,遍历其后面的数字,直到序列大于或等于S。时间复杂度O(n2),不推荐。

public class Solution {
    public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        for(int i = 1; i < sum; i++) {
            int count = 0;
            ArrayList<Integer> list = new ArrayList<Integer>();
            for(int j = i; j < sum; j++) {
                list.add(j);
                count += j;
                if(count == sum) {
                    result.add(list);
                    break;
                }
                if(count > sum)
                    break;
            }
        }
        return result;
    }
}
解法二:

设置两个指针low和high分别指向1和2,通过high++计算low与high间的序列和,
若low + high == sum, 则将序列记录下来,low++
若low + high > sum, 则low++
若low + high < sum, 则high++

时间复杂度为O(N)

public class Solution {
    public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        int low = 1, high = 2;
        //循环次数控制,不需要low < sum
        while(low < (1 + sum) / 2) {
            int count = (low + high) * (high - low + 1) / 2;
            if(count == sum) {
                ArrayList list = new ArrayList<Integer>();
                for(int i = low; i <= high; i++) {
                    list.add(i);
                }
                result.add(list);
                low++;
            }
            else if(count < sum)
                high++;
            else 
                low++;
        }
        return result;
    }
}

计算时的求和语句:int count = (low + high) * (high - low + 1) / 2;若low与high相差较大时,该乘法比使用循环加法要快。
如果要使用循环加法,可以进行优化,在之前的和的基础上进行运算,避免大量重复计算。

public class Solution {
    public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        int low = 1, high = 2;
        //循环次数控制,不需要low < sum
        int count = low + high;
        while(low < (1 + sum) / 2) {
            while(count < sum) {
                high++;
                count += high;
            }
            if(count == sum) {
                ArrayList list = new ArrayList<Integer>();
                for(int i = low; i <= high; i++) {
                    list.add(i);
                }
                result.add(list);
            }
            count -= low;
            low++;
        }
        return result;
    }
}
解法三:

直接利用数学方法。
count = (low + high) (high - low + 1) / 2
推出(low + high) (high - low + 1) = 2 * count
设(low + high) (high - low + 1) = k * l (k >= l)
则low + high = k,high - low + 1 = l
推出
high = (k + l -1) / 2
low = (k - l + 1) / 2
我们可以推算出k与l,继而获得low与high

public class Solution {
    public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        int max = (int)Math.sqrt(2 * sum);
        for(int l = max; l > 1; l--) {
            if((2 * sum) % l == 0) {
                int k = (2 * sum) / l;
                if((k + l) % 2 == 1) {
                    int low = (k - l + 1) / 2;
                    int high = (k + l - 1) / 2;
                    ArrayList<Integer> list = new ArrayList<Integer>();
                    for(int i = low; i <= high; i++) 
                        list.add(i);
                    result.add(list);
                }
            }
        }
        return result;
    }
}

由于k与l越接近时low越小,导致获得的list顺序也是按照low从小到达的顺序获得的,满足题目要求。
时间复杂度为O(logN)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容