剑指Offer-40 有序数组的两数和(首尾逼近法)

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。(对应每个测试案例,输出两个数,小的先输出。)

  • 对于 i + j = sum,当i和j大小越悬殊的的时候 i*j越小,如 i = 1,j=sum-1。
    我们可以根据上面的规律,从数组的两头开始寻找。

首尾相加法

  1. 用i,j两个指针,分别指向首尾
  2. i从首开始遍历,j从尾开始遍历
  3. 对于每个i,j都需要全部寻找一遍
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<>();
        for(int i=0;i<array.length;i++){
            for(int j=array.length-1;j>=0;j--){
                if(array[i] + array[j] == sum){
                    list.add(array[i]);
                    list.add(array[j]);
                    return list;
                }
            }
        }
        return list;
    }

这里面存在一个问题,如果a[i] + a[j] < sum,j依然要 j–,并继续遍历,那么接下来的运算没有意义了。时间复杂度O(n2)

首尾逼近法

这是对上一种方法的改进,核心思想还是从首尾开始找,改变的是通过判断a[i] + a[j]sum的大小关系决定是i改变还是j改变。类似于剪纸的思想。

  • 数列满足递增,设两个头尾两个指针i和j,
  • ai + aj == sum,就是答案(相差越远乘积越小)
  • ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j --
  • 若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i ++
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<>();
        int i = 0, j = array.length -1;
        while(j >= i){
            int temp = array[i] + array[j];
            if(temp == sum){
                list.add(array[i]);
                list.add(array[j]);
                break;
            }else if(temp > sum){
                j--;
            }else {
                i ++;
            }
        }
        return list;
    }

时间复杂度O(n)

    原文作者:北国雪WRG
    原文地址: https://www.jianshu.com/p/ff95379e93ad
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞