[Capriccio 7] the focus of nSum problem is reasonable de duplication

Reference: a method to solve the nSum problem

In an array, given a targer, find n numbers so that their sum is target

For example, the array is [- 4, - 4, - 3, - 2, - 1,0,1,2,3,4,4]
When n = 2, target=0, the results are [- 4,4], [- 3,3]
When n = 3, target=0, the results are [- 4,1,3], [- 3,1,2]
When n = 4, target=1, the result is [- 4, - 2,3,4]

There are different combinations of subscripts,
You can also ask how many different combinations of array elements there are

1. Sum of two numbers

15. Sum of three

18. Sum of four numbers

Sum of two numbers

The hash table method can be used to solve this problem
Remember that the current element is a and traverse from the beginning,

  • If the element target - a does not exist in the hash table, add the element value key and subscript as value to the hash table
  • If the element target - a exists in the hash table, it exists. Just return the subscripts of the two elements.

If it is not found at the end of the array, it means that there is no

You can use sorting plus double pointers

First, sort the whole, and then traverse the first and second pointers. The method is similar to binary search.

Difficulty upgrade

In nums, there may be multiple pairs of child elements whose sum is equal to target. Please return all element pairs whose sum is target in your algorithm, and there can be no duplication.
For example, if the input is num = [1,3,1,2,2,3] and target = 4, the result returned by the algorithm is: [[1,3], [2,2]].
For the modified problem, it is not difficult to return the value of the element instead of the corresponding index. The key difficulty is that there may be multiple pairs with a sum of target, which cannot be repeated. For example, in the above example, even if [1,3] and [3,1] are repeated, they can only be counted once.

For the example mentioned above, because you are asking for the value of array elements, you can sort them first
After sorting, [1, 1, 2, 2, 3, 3]
Because it is required that the result set is composed of the same element values only once

Using double pointers, in the traversal process, gradually reduce the distance between the two pointers. If the pointers meet and no new result set is generated, it means that the current element cannot meet the requirements of the topic, then start from the next element.

// Pseudo code
    // Sort the array first
    sort(nums);
    int lo = 0, hi = nums.size() - 1;
    while (lo < hi) {
        int sum = nums[lo] + nums[hi];
        // Move the left and right pointers according to the comparison between sum and target
        if      (sum < target) lo++;
        else if (sum > target) hi--;
        else {
            res.add({lo, hi});
            lo++; hi--;
        }
    }
    return res;
}

This will add the set of repeated element values to the result set, so we must find a way to duplicate it in the process of adding the result set.
Either add the result set to remove the duplicate, or remove the duplicate before adding. Write it yourself to know how troublesome it is to remove the duplicate after adding the result. Therefore, it is also a general processing scheme to remove the duplicate reasonably before adding.

Analyze the causes of repeated results and find that,
[1,1,2,2,3,3],target=4

The problem lies in the if branch of the sum == target condition. After adding a result to res, lo and hi should not only change 1, but should skip all repeated elements

//Pseudo code
while (lo < hi) {
    int sum = nums[lo] + nums[hi];
    // Record the initial corresponding values of the indexes lo and hi
    int left = nums[lo], right = nums[hi];
    if (sum < target)      lo++;
    else if (sum > target) hi--;
    else {
        res.add({left, right});
        // Skip all duplicate elements
        while (lo < hi && nums[lo] == left) lo++;
        while (lo < hi && nums[hi] == right) hi--;
    }
}

In this way, you can ensure that an answer is added only once, and the repeated results will be skipped to get the correct answer. However, inspired by this idea, the first two if branches can also do some efficiency optimization and skip the same elements

//Pseudo code
while (lo < hi) {
        int sum = nums[lo] + nums[hi];
        int left = nums[lo], right = nums[hi];
        if (sum < target) {
            while (lo < hi && nums[lo] == left) lo++;
        } else if (sum > target) {
            while (lo < hi && nums[hi] == right) hi--;
        } else {
            res.add({left, right});
            while (lo < hi && nums[lo] == left) lo++;
            while (lo < hi && nums[hi] == right) hi--;
        }
    }
    return res;

Sum of three

For the sum of three numbers equal to target, it can be regarded as a given number plus the remaining numbers, and the sum of two numbers is equal to target minus the given number

public List<List<Integer>> threeSum( int[] nums ) {
        List<List<Integer>> res = new ArrayList<>();
        //Sort first
        Arrays.sort( nums );
        // Start with each element and find out if there are two corresponding numbers that add up to 0
        for ( int i = 0; i < nums.length; i++ ) {
        //Special operation when target=0. If the topic is given to target, there is no such step
            if ( nums[i] > 0 ) {
                return res;
            }
            //This step is de duplication if the current element is equal to the previous element
            // Then, for elements with the same value, if there are two other numbers that meet the requirements of the topic,
            ///It must be the same as the result set of the previous number
            if ( i > 0 && nums[i] == nums[i - 1] ) {
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
             while ( right> left ) {
                int sum = nums[i] + nums[left] + nums[right];
                if ( sum > 0 ) {
                    right--;
                } else if ( sum < 0 ) {
                    left++;
                } else if ( sum == 0 ) {
                    res.add( Arrays.asList( nums[i], nums[left], nums[right] ) );
                    while ( right > left && nums[right] == nums[right - 1] ) {
                        right--;
                    }
                    while ( right > left && nums[left] == nums[left + 1] ) {
                        left++;
                    }
                    right--;
                    left++;
                }
            }

        }
        return res;
    }

Sum of four numbers


In the same way, four numbers can be simplified to, given a number, find three of the remaining numbers equal to
target - given number

        public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
       
        for (int i = 0; i < nums.length; i++) {
        //If the current number is equal to the previous number, skip because the result set already exists,
        //Or it must not exist
            if (i > 0 && nums[i - 1] == nums[i]) {
                continue;
            }
            for (int j = i + 1; j < nums.length; j++) {
//The principle of weight removal is the same as above
                if (j > i + 1 && nums[j - 1] == nums[j]) {
                    continue;
                }
                
                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }

summary

In the sum of two numbers, violent method O (n^2)
Use double pointers to increase a limited number of variables to one dimension

In the sum of three, violent method O (n^3)
Change to multiple pointers, add a limited number of variables, and reduce to two dimensions

Of the sum of four, violent method O (n^4)
Change to multiple pointers, add a limited number of variables, and reduce to three-dimensional

It is equivalent to using multiple pointers, which can reduce the original algorithm by one latitude.

Keywords: Algorithm data structure

Added by merrydown on Sun, 26 Dec 2021 19:21:02 +0200