Wednesday, June 15, 2022

1982. Find Array Given Subset Sums

 一刷 06/2022

Version #1 Divide and Conquer

Time The total runtime will be O(NlogN), where N = 2^n

Space O(N)

Runtime: 896 ms, faster than 5.00% of Java online submissions for Find Array Given Subset Sums.
Memory Usage: 50.7 MB, less than 80.00% of Java online submissions for Find Array Given Subset Sums.

class Solution {

    public int[] recoverArray(int n, int[] sums) {

        // If we are adding a new element B to the existing set A1, A2,..., Ai

        // We'll be doubling the set with new sums A1+B, A2+B,...,Ai+B

        // So we want to get number B and partition the sums into two groups: with and without B

        // How to decide B

        // If all sums are positive, then the smallest sum (except for 0) is B itself

        // If some sums are negative, then we'll know that the min sum is the sum of all negative numbers, and the second minimum sum is either sum of all negative numbers except for the largest negative number, or the sum of all negative numbers with the smallest positive number

        // So if we do nums[1] - nums[0] we'll get either -x of a negative x, or x of a positive x

        // We try both x = nums[1] - nums[0] and x = nums[0] - nums[1], and check if they are successfully partition the array

        return recoverHelper(n, sums).stream().mapToInt(Integer::intValue).toArray();

    }

    

    private static int[] signs = new int[]{1, -1};

    private List<Integer> recoverHelper(int n, int[] sums) {

        if (sums == null) {

            return new ArrayList<>();

        }

        if (n == 1) {

            if (sums[1] != 0 && sums[0] != 0) {

                return null;

            }

            // Only 1 number, it could be positive or negative

            int num = sums[0] == 0 ? sums[1] : sums[0];

            return new ArrayList<>(Arrays.asList(num));

        }

        Arrays.sort(sums);

        List<Integer> result = null;

        for (int sign : signs) {

            int num = sign * (sums[1] - sums[0]);

            int[] half = partition(num, sums);

            if (half == null) {

                continue;

            }

            // Can partition, go to the next layer

            List<Integer> subResult = recoverHelper(n - 1, half);

            if (subResult == null) {

                continue;

            }

            result = new ArrayList<>();

            result.add(num);

            result.addAll(subResult);

        }

        return result;

    }

    

    private int[] partition(int num, int[] sums) {

         int[] half = new int[sums.length / 2];

        int i = 0;

        Map<Integer, Integer> count = new HashMap<>();

        for (int sum : sums) {

            count.put(sum, count.getOrDefault(sum, 0) + 1);

        }

        // sums is sorted in ascending order

        // If num < 0, we should start from the smallest numbers

        // If num > 0, we should start from the largest numbers

        int index = num > 0 ? sums.length - 1 : 0;

        boolean hasZero = false;

        while ((num > 0 && index >= 0) || (num <= 0 && index < sums.length)) {

            int sum = sums[index];

            if (count.getOrDefault(sum, 0) > 0) {

                // If not exists, continue to the next element

                int pair = sum - num;

                count.put(sum, count.get(sum) - 1); // decrement the sum count by 1

                if (!count.containsKey(pair) || count.get(pair) == 0) {

                    return null; // cannot partition

                }

                count.put(pair, count.get(pair) - 1);

                half[i++] = pair;

                if (pair == 0) {

                    hasZero = true;

                }

            }

            

            //

            if (num > 0) {

                index--;

            } else {

                index++;

            }

        }

        return hasZero ? half : null;

    }

}

No comments:

Post a Comment