本文参考自《剑指offer》一书,代码采用Java语言。html
更多:《剑指Offer》Java实现合集 java
输入一个正数s,打印出全部和为s的连续正数序列(至少含有两个数)。例如输入15,因为1+2+3+4+5=4+5+6=7+8=15,因此结果打印出3个连续序列1~五、4~6和7~8。post
指针法:学习
相似(57-1) 和为s的两个数字的方法,用两个指针small和big分别表明序列的最大值和最小值。令small从1开始,big从2开始。测试
当从small到big的序列的和小于s时,增长big,使序列包含更多数字;(记得更新序列之和)ui
当从small到big的序列的和大于s时,增长small,使序列去掉较小的数字;(记得更新序列之和)url
当从small到big的序列的和等于s时,此时获得一个知足题目要求的序列,输出,而后继续将small增大,日后面找新的序列。指针
序列最少两个数字,所以,当small到了s/2时,就能够结束判断了。code
数学分析法:htm
对于一个长度为n的连续序列,若是它们的和等于s,有:
1)当n为奇数时,s/n刚好是连续序列最中间的数字,即n知足 (n&1)==1 && s%n==0
2)当n为偶数时,s/n刚好是连续序列中间两个数字的平均值,小数部分为0.5,即n知足 (s%n)*2==n (判断条件中包含了n为偶数的判断)
获得知足条件的n后,至关于获得了序列的中间数字s/n,因此能够获得第一个数字为 (s / n) - (n - 1) / 2,结合长度n能够获得全部数字。
此外,在什么范围内找n呢?咱们知道n至少等于2,那至多等于多少?n最大时,序列从1开始,根据等差数列的求和公式根据等差数列的求和公式:S = (1 + n) * n / 2,能够获得n应该小于sqrt(2s),因此只须要从n=2到sqrt(2s)来判断知足条件的n,继而输出序列。
测试算例
1.功能测试(存在/不存在和为s的序列)
2.边界值测试(s=3)
方法一:
//题目:输入一个正数s,打印出全部和为s的连续正数序列(至少含有两个数)。 //例如输入15,因为1+2+3+4+5=4+5+6=7+8=15,因此结果打印出3个连续序列1~五、 //4~6和7~8。 public class ContinuousSquenceWithSum { //方法一:采用两个指针的方法 public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer> > sequenceList = new ArrayList<ArrayList<Integer> >(); if(sum<=0) return sequenceList; int small = 1; int big = 2; int curSum = small+big; while(small <= sum/2){ if(curSum == sum){ ArrayList<Integer> sequence = new ArrayList<Integer>(); for(int i=small;i<=big;i++) sequence.add(i); sequenceList.add(sequence); curSum-=small; small++; //这两行位置前后要注意 } if(curSum < sum){ big++; curSum+=big; } if(curSum > sum){ curSum-=small; small++; } } return sequenceList; } }
方法二:
//方法二:数学分析法 public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer> > sequenceList = new ArrayList<ArrayList<Integer> >(); if(sum<=0) return sequenceList; for(int n=(int) Math.sqrt(2*sum);n>=2;n--){ if(((n&1)==1 && sum%n==0) || ((n&1)==0 && (sum%n)*2==n)){ ArrayList<Integer> sequence = new ArrayList<>(); for (int j = 0, k = (sum / n) - (n - 1) / 2; j < n; j++, k++) { sequence.add(k); } sequenceList.add(sequence); } } return sequenceList; }
1.仍是利用两个指针,这个技巧要学会
2.代码中求连续序列的和,并无每次遍历计算,而是根据每次操做的状况而在以前的结果上进行加减,能够提升效率,值得学习
3.题目57-1) 和为s的两个数字中的指针是从两端开始,本题指针从1,2开始,注意指针的初始设置。
4.方法二中,当s/n的余数为0.5时,s%n的结果是n/2,而不是1。