题目描述
和为S的连续正数序列:输入一个正数S,打印所有和为S的连续正数序列(至少含有两个数)
例如,输入15,有1+2+3+4+5和4+5+6和7+8三种情况,所以打印三个序列。


思路:如果做过57.1题,和为S的两个数字这道题,就可以借鉴做法。即利用两个指针记录首尾,然后进行相关操作。本题使用一前一后两个指针,两个指针范围内的数字之和cur和给定的sum进行判断。
过程:以15为例,假如我们找到了{4,5,6},需要找下一个解,其实我们可以有3个选择,移动first,移动last,或者两个指针都移动。书中选择移动last.这里我选择的是两个指针都移动,并且同步更新当前的范围之和cur。如果cur与sum相同,那不用说,直接往ArrayList里装,如果当前移动后的cur超过了sum,说明要减去某个数,则first后移,同步更新cur,如果移动后的cur小于sum,则说明要加上某个数,则last后移。移动指针并更新cur后,再次进入while,判断是否相同。
细节:first超过sum的一半时,就说明没有这样的序列了,就可以结束了,而不用一直让first++.

写法1:

根据offer书上的写法,汲取了一些优点,改成了自己的版本,和很多人写法不同的是,我在修改指针的同时,就更新了和,而不用再调用一个求和函数。

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
       ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
       if(sum < 3)//至少为3,分成1和2
           return result;
        int first = 1;
        int mid = (1+sum)/2;//边界条件
        int last = 2;
        int cur = first + last;//cur是当前的和,初始为3
        while(first < mid){
            ArrayList<Integer> list1 = new ArrayList<>();
            if(cur == sum){//这里碰到了相同
                for(int i = first; i <= last; i++){
                    list1.add(i);
                }
                result.add(list1);//序列装到list,然后list再成为result的一个元素
                first++;//同时维护指针
                last++;
                cur = cur+last-first+1;//更新和
            }
            else if(cur > sum && first < mid){//大于的话,也进行更新,然后进入while
                cur = cur - first;
                first++;
            }
            else{//小于的时候,更新指针,更新和,然后进入while
                 last++;
                 cur += last;
            }     
        }
        return result;
    }
}

写法2:剑指offer书上改成java版本

  这里对书上的写法进行解释。如果等于,直接装给ArrayList。那么下一次移动的时候,只移动last角标。(这样做也有一点好处)。移动last后,就会出现cur大于sum时,移动first,再次移动first时就可能出现相同,这里可以判断也可以不判断(书中选择在这里判断了,所以看到这里又有一个判断并装给ArrayList的动作),在不断移动first中,又可能出现cur小于sum(移动过头了,好好理解这个过程!)。小于后,就再移动last,并更新cur,就进入下一次while了。
  可以看到,书上将大于,小于,等于三种情况,分成了两次移动last,一次移动first,所以在两次移动last的部分,进行了代码复用。(last++只出现了一次,这就是我刚刚说的好处^_^)
  而我的是分成了三种,相同更新两个指针,大于时更新first,小于时更新last。不仅如此,书上小于这种情况时,还一步判断了是否有解的情况(又使用了while,while里又套了个while)。而我是将所有的情况分析都留个同一个while判断.

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
       ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
       if(sum < 3) //最低也是1和2,所以sum至少为3.
           return result;
        int first = 1;
        int mid = (1+sum)/2;//标记结束的标志 first=mid说明无解
        int last = 2;
        int cur = first + last;//cur为当前的两个指针之间所有数的和,初始值就是3
        while(first < mid){
            ArrayList<Integer> list1 = new ArrayList<>();//为了避免重复添加,必须先new
            if(cur == sum){//和相等,则用一个list记录当前整个序列
                for(int i = first; i <= last; i++){
                    list1.add(i);
                }
                result.add(list1);//再倒一手给result。
            }
            //如果不相等,就可能大于或者小于。
            while(cur > sum && first < mid){//如果大于了,并且first没超标,说明还能移动
                ArrayList<Integer> list2 = new ArrayList<>();
                cur = cur - first;//first角标移动时,要从cur中减去这个数
                first++;//这里采用的是,先减,再动角标。先动角标就应该写成cur=cur-first+1;
                if(cur == sum){//一旦移动后,就可能相等,书中选择在这里再次判断,而我不是。
                     for(int i = first; i <= last; i++){
                    list2.add(i);
                }
                result.add(list2);
                }                   
            }
           //此时cur<sum,就应该让last++,然后更新cur
           //下面这两句代码进行了复用,等于以及小于的情况,都是这个时候才维护指针。
           last++;
           cur += last;
        }
        return result;
    }
}

总结:offer书上的写法不太容易第一时间理解。但是这个思想肯定没有问题,根据这个思想形成自己的解题思路,正常写就可以写出来。