剑指Offer对答如流系列 - 和为s的数字

Posted jefferychenxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer对答如流系列 - 和为s的数字相关的知识,希望对你有一定的参考价值。

面试题57:和为s的数字

题目描述

问题(1)和为s的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。

问题(2)为s的连续正数序列

输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结果打印出3个连续序列1~5、4~6和7~8。

问题分析

问题(1)

考虑到数列递增,我们设置两个头尾两个索引i和j,

  1. 若ai + aj == sum,就是答案(相差越远乘积越小)

  2. 若ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j -= 1

  3. 若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i += 1

由于是从两边往中间移动,所以不会有跳过的情况,时间复杂度为O(n)。

问题(2)

顺着问题(1)的思路来就是

  1. 当从i到j的序列的和小于sum时,增加j,使序列包含更多数字;(记得更新序列之和)

  2. 当从i到j的序列的和大于sum时,减少i,使序列去掉较小的数字;(记得更新序列之和)

  3. 当从i到j的序列的和等于sum时,此时得到一个满足题目要求的序列,输出,然后继续将i增大,往后面找新的序列。

序列要求最少两个数字,因此,当i到了sum/2时,就可以结束了。

不过,问题(2)也可以不借鉴问题(1)的思路,采用数学分析法

对于一个长度为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)

    public ArrayList<Integer> FindNumbersWithSum(int [] array, int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(array==null || array.length<=0) {
            return list;
        }
        int low=0;
        int high=array.length-1;
        while(low<high){
            if(array[low]+array[high]==sum){
                list.add(array[low]);
                list.add(array[high]);
                break;
            } else if(array[low]+array[high]<sum) {
                low++;
            } else {
                high--;
            }
        }
        return list;
    }

问题(2)

思路一:

     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<>();
                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;
    }

以上是关于剑指Offer对答如流系列 - 和为s的数字的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer系列45---和为s的两个数字

剑指Offer对答如流系列 - 数组中数字出现的次数

剑指offer系列46---和为s的连续正数序列

剑指Offer对答如流系列 - 把数组排成最小的数

剑指Offer对答如流系列 - 圆圈中最后剩下的数字

剑指Offer对答如流系列 - 数字序列中某一位的数字