剑指41.和为S的连续正数序列

题目描述

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

输出描述:

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

思路

思路1:滑动窗口法(区间连续,双指针同向移动)。用两个数small和big分别表示序列的最小值和最大值,首先把small初始化为1,big初始化为2.如果从small到big的序列的和大于s,则从序列中去掉较小的值,也就是增大small的值。如果从small到big的序列的和小于s,则可以增大big,让这个序列包含更多的数字。因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2为止。

 

思路2:还可以尝试数学分析法。

 

解法1

import java.util.ArrayList;
public class Solution {
    // 版本1
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer> >  res = new ArrayList<>();
        int small = 1, big = 2;  //两个起点,相当于动态窗口的两边
        while (small < big){ // small < (sum + 1) / 2  也能通过
            int tempSum = (small + big)*(big - small + 1) / 2;
            if (tempSum == sum){
                ArrayList<Integer> list = new ArrayList<>(); // 每次用的时候new一个,这样就不需要用完list.clear()了
                for (int i = small; i <= big; i++) {
                    list.add(i);
                }
                res.add(list);
                big++;
            }else if (tempSum > sum){  //如果当前窗口内的值之和大于sum,那么左边窗口右移一下
                small++;
            }else{  //如果当前窗口内的值之和小于sum,那么右边窗口右移一下
                big++;
            }
        }
        return res;
    }
    // 版本2
    // 与版本1相比不同之处:求连续序列的和,因此可以在前一个序列和的基础上求操作之后的序列的和。
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer> >  res = new ArrayList<>();
        int small = 1, big = 2;  //两个起点,相当于动态窗口的两边
        int tempSum = small + big;
        while (small < big){ // small < (sum + 1) / 2  也能通过
            if (tempSum == sum){
                ArrayList<Integer> list = new ArrayList<>();
                for (int i = small; i <= big; i++) {
                    list.add(i);
                }
                res.add(list);
                big++;
                tempSum += big;
            }else if (tempSum > sum){  //如果当前窗口内的值之和大于sum,那么左边窗口右移一下
                tempSum -= small;
                small++;
            }else{  //如果当前窗口内的值之和小于sum,那么右边窗口右移一下
                big++;
                tempSum += big;
            }
        }
        return res;
    }
}

Note: while循环条件最好用 small < (sum+1)/2 这样可以提前结束。

 

解法2

M一下,以后再做可以试试用数学的思路解一下。

 

自己写的暴力遍历法

import java.util.ArrayList;
public class Solution {
    // 思路就是根据等差数列求和公式(count*(a1+an))/2,遍历起始位置a1,然后遍历count,看是否满足题意
    // 测试用例,输入15;输出1~5 、4~6 、7~8
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<>();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= sum / 2 + 1; i++) {
            int num = i;
            int counts = sum / 2 + 1;
            while (counts >= 2){
                if (counts*(num+num+counts-1)/2 == sum){  // 核心
                    for (int j = num; j <=num+counts-1;j++)
                        list.add(j);
                    res.add(new ArrayList<>(list));
                    list.clear();
                    break;
                }
                counts--;
            }
        }
        return res;
    }
}

 

收获

如果需要多次使用ArrayList添加数据,可以在每次用的时候new一个新的,这样就不用共用一个,用完list.clear()了。

 

posted @ 2020-08-25 18:22  不学无墅_NKer  阅读(202)  评论(0编辑  收藏  举报