How to find all ordered pairs of elements in array of integers whose sum lies in a given range of value

前端 未结 8 1047
抹茶落季
抹茶落季 2021-02-05 21:58

Given an array of integers find the number of all ordered pairs of elements in the array whose sum lies in a given range [a,b]

Here is an O(n^2) solution for the same <

8条回答
  •  一向
    一向 (楼主)
    2021-02-05 22:17

    With some constraints on the data we can solve problem in linear time (sorry for Java, I'm not very proficient with Python):

    public class Program {
        public static void main(String[] args) {
            test(new int[]{-2, -1, 0, 1, 3, -3}, -1, 2);
            test(new int[]{100,200,300}, 300, 300);
            test(new int[]{100}, 1, 1000);
            test(new int[]{-1, 0, 0, 0, 1, 1, 1000}, -1, 2);
        }
    
        public static int countPairs(int[] input, int a, int b) {
            int min = Integer.MAX_VALUE;
            int max = Integer.MIN_VALUE;
            for (int el : input) {
                max = Math.max(max, el);
                min = Math.min(min, el);
            }
            int d = max - min + 1; // "Diameter" of the array
            // Build naive hash-map of input: Map all elements to range [0; d]
            int[] lookup = new int[d];
            for (int el : input) {
                lookup[el - min]++;
            }
            // a and b also needs to be adjusted
            int a1 = a - min;
            int b1 = b - min;
            int[] counts = lookup; // Just rename
            // i-th element contain count of lookup elements in range [0; i]
            for (int i = 1; i < counts.length; ++i) {
                counts[i] += counts[i - 1];
            }
            int res = 0;
            for (int el : input) {
                int lo = a1 - el; // el2 >= lo
                int hi = b1 - el; // el2 <= hi
                lo = Math.max(lo, 0);
                hi = Math.min(hi, d - 1);
                if (lo <= hi) {
                    res += counts[hi];
                    if (lo > 0) {
                        res -= counts[lo - 1];
                    }
                }
                // Exclude pair with same element
                if (a <= 2*el && 2*el <= b) {
                    --res;
                }
            }
            // Calculated pairs are ordered, divide by 2
            return res / 2;
        }
    
        public static int naive(int[] ar, int a, int b) {
            int res = 0;
            for (int i = 0; i < ar.length; ++i) {
                for (int j = i + 1; j < ar.length; ++j) {
                    int sum = ar[i] + ar[j];
                    if (a <= sum && sum <= b) {
                        ++res;
                    }
                }
            }
            return res;
        }
    
        private static void test(int[] input, int a, int b) {
            int naiveSol = naive(input, a, b);
            int optimizedSol = countPairs(input, a, b);
            if (naiveSol != optimizedSol) {
                System.out.println("Problem!!!");
            }
        }
    }
    

    For each element of the array we know the range in which second element of the pair can lay. Core of this algorithm is giving the count of elements in range [a; b] in O(1) time.

    Resulting complexity is O(max(N, D)), where D is difference between max and min elements of the array. If this value is same order as N - complexity is O(N).

    Notes:

    • No sorting involved!
    • Building lookup is required to make algorithm work with negative numbers and make second array as small as possible (positively impacts both memory and time)
    • Ugly condition if (a <= 2*el && 2*el <= b) is required because algorithm always counts pairs (a[i],a[i])
    • Algorithm requires O(d) additional memory which can be a lot.

    Another linear algorithm would be radix sort + linear pair counting.

    EDIT. This algorithm can be really good in case if D is considerably smaller than N and you are not allowed to modify the input array. Alternative option for this case would be slightly modified counting sort with allocation of counts array (additional O(D) memory) but without populating sorted elements back to input array. It's possible to adapt pair counting to use counts array instead of full sorted array.

提交回复
热议问题