寻找和固定的两个或三个数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhufenghao/article/details/71156782

题目

快速找出一个包含 n 个整数的数组中所有两个数的组合,让这两个数之和等于一个给定的值 sum 。输入 sum n ,接着是所有整数,要求输出所有两个数的组合。

分析1

如果直接遍历穷举将会得到 O(n2) 的时间复杂度。对于每一个元素 array[i] 来说,可以将问题转化为 sumarray[i] 是否在数组中,而如果事先将数组排序则可以利用二分查找来完成,此时算法的时间复杂度为 O(nlog(n)+log(n))=O(nlog(n))

另外一个思路同样是将数组排序,接着设置两个指针 i j 分别从两端向中间遍历数组,如果 array[i]+array[j]=sum ,则 i 后移且 j 前移;如果 array[i]+array[j]<sum ,则 i 后移 j 不动; array[i]+array[j]>sum ,则 i 不动 j 前移;最终找到所有这样的两个数的组合。这样遍历的依据是,对于 k=0i1 ,一定有 array[k]+array[j]array[i]+array[j] i 后移表示和递增,而对于 l=j+1n1 ,一定有 array[i]+array[l]array[i]+array[j] j 前移表示和递减,从而利用双指针遍历就可以依次找出所有两个数的组合。这样算法的时间复杂度依然为 O(nlog(n)+n)=O(nlog(n))

能否让时间复杂度进一步减小呢?利用哈希往往能用空间换取时间。不用排序,将所有整数放入哈希表中,对于每一个元素 array[i] 去查表 sumarray[i] 是否存在,这只需要 O(1) 的时间,从而算法的时间复杂度为 O(n) ,但是对于重复的组合需要额外去记录。

代码1

import java.util.*;

public class Sum2 {
    static void sum2bs(int[] array, int sum) {
        Arrays.sort(array);
        int n = array.length;
        int remain, pos;
        for (int i = 0; i < n; i++) {
            remain = sum - array[i];
            pos = binarySearch(array, remain, i);// 如果一个数只使用一次则左边界为i+1
            if (pos != -1)
                System.out.println(array[i] + " " + remain);
        }
    }

    // 数组从左向右遍历,设置查找的左边界就可以避免重复组合
    static int binarySearch(int[] array, int target, int left) {
        int i = left, j = array.length - 1;
        int mid;
        while (i <= j) {
            mid = i + (j - i) / 2;
            if (array[mid] == target) {
                return mid;
            } else if (array[mid] < target) {
                i = mid + 1;
            } else {
                j = mid - 1;
            }
        }
        return -1;
    }

    static void sum2bt(int[] array, int sum) {
        Arrays.sort(array);
        int n = array.length;
        int i = 0, j = n - 1;
        while (i <= j) {// 如果一个数只使用一次则i<j
            if (array[i] + array[j] == sum) {
                System.out.println(array[i] + " " + array[j]);
                i++;
                j--;
            } else if (array[i] + array[j] < sum) {
                i++;
            } else {
                j--;
            }
        }
    }

    static void sum2hash(int[] array, int sum) {
        int n = array.length;
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < n; i++) {
            set.add(array[i]);
        }
        int remain;
        for (int i = 0; i < n; i++) {
            remain = sum - array[i];
            if (set.contains(remain))
                System.out.println(array[i] + " " + remain);
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int sum = sc.nextInt();
        int n = sc.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = sc.nextInt();
        }
        sum2bs(array, sum);
    }
}

扩展

将找出所有两个数的组合改为找出所有三个数的组合,其余不变。

分析2

找三个数可以使用同样的思路,但是使用二分查找和双指针遍历就会有不同的复杂度。考虑到 remain2=sumarray[i] 只能有 n 个值,对于每个 remain2 可以转化为找和为 remain2 的两个数的组合,如果使用二分查找时间复杂度为 O(nlog(n)+nnlog(n))=O(n2log(n)) 。但是如果使用双指针遍历,由于找两个数的组合只需要 O(n) 的时间,故时间复杂度为 O(nlog(n)+nn)=O(n2)

代码2

import java.util.*;

public class Sum3 {
    static void sum3bs(int[] array, int sum) {
        Arrays.sort(array);
        int n = array.length;
        int remain1, remain2, pos;
        for (int i = 0; i < n; i++) {
            remain2 = sum - array[i];
            for (int j = i; j < n; j++) {
                remain1 = remain2 - array[j];
                pos = binarySearch(array, remain1, j);// 如果一个数只使用一次则左边界为j+1
                if (pos != -1)
                    System.out.println(array[i] + " " + array[j] + " " + remain1);
            }
        }
    }

    // 数组从左向右遍历,设置查找的左边界就可以避免重复组合
    static int binarySearch(int[] array, int target, int left) {
        int i = left, j = array.length - 1;
        int mid;
        while (i <= j) {
            mid = i + (j - i) / 2;
            if (array[mid] == target) {
                return mid;
            } else if (array[mid] < target) {
                i = mid + 1;
            } else {
                j = mid - 1;
            }
        }
        return -1;
    }

    static void sum3bt(int[] array, int sum) {
        Arrays.sort(array);
        int n = array.length;
        int remain2, j, k;
        for (int i = 0; i < n; i++) {
            remain2 = sum - array[i];
            j = i;// 如果一个数只使用一次则j=i+1
            k = n - 1;
            while (j <= k) {// 如果一个数只使用一次则条件为j<k
                if (array[j] + array[k] == remain2) {
                    System.out.println(array[i] + " " + array[j] + " " + array[k]);
                    j++;
                    k--;
                } else if (array[j] + array[k] < remain2) {
                    j++;
                } else {
                    k--;
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int sum = sc.nextInt();
        int n = sc.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = sc.nextInt();
        }
        sum3bs(array, sum);
    }
}

猜你喜欢

转载自blog.csdn.net/zhufenghao/article/details/71156782