【LeetCode】Detailed explanation of the sum of four numbers 18. 4Sum Given an array nums of n integers and an integer target, are there elements


foreword

Today is the weekend, and the company is on weekends. I thought there would be a lot of people staying. Haha, but I was so naive. It seems that there are only two or three people on our floor.
As for me, because I haven't done the questions for a while, I'm doing it in the company. Today I bring you the 18th question of LeetCode, the sum of four numbers. It is recommended to read the blog on the sum of three numbers that I wrote before, because The ideas are all the same.
Poke me to jump to the explanation of the sum of three numbers?


text

original title

Link: Sum of Four

Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note:
The solution set must not contain duplicate quadruplets.
Example:
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]

The gist of the title
Given an integer array nums and a target value, find the case where the sum of all four numbers equals target.

Idea 1

Of course, it is the most common and violent method. The four-layer loop traverses all cases, the time complexity is O(N^4) , and the code is very simple.

code

class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        if (nums == null || nums.length < 4) return new LinkedList<>();
        
        // 思路1:使用四层循环
        Set<List<Integer>> result = new HashSet<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
    
    
            for (int j = i + 1; j < nums.length; j++) {
    
    
                for (int k = j + 1; k < nums.length; k++) {
    
    
                    for (int l = k + 1; l < nums.length; l++) {
    
    
                        if (nums[i] + nums[j] + nums[k] + nums[l] == target) {
    
    
                            result.add(Arrays.asList(nums[i], nums[j], nums[k], nums[l]));
                        }
                    }
                }
            }
        } 
        
        return new ArrayList(result);
    }
}

Code explanation

The set data structure is used to avoid adding duplicates, and then the Arrays.sort(nums);sorting is used first. The purpose of sorting is to facilitate deduplication. The blog of the sum of three numbers also mentioned it.
Submitting code, time and space are very slow, and sometimes there will be a timeout!
insert image description here

Idea 2

Idea 2 is the same as the sum of three numbers, reduce one layer of loop, use three layers of loop to traverse each situation, and use set to judge whether there is a difference,The set here stores a value, not a List like the previous idea.
For example, the number of loops in the first layer is , the number of nums[i]loops in the second layer is , and the number of loops in the nums[j]third layer is . Then it is judged whether there is a difference nums[k]between the target and the sum of the first three layers in the set. If it exists , it is added to the list. .nums[i] + nums[j] + nums[k]target - nums[i] + nums[j] + nums[k]

code

class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        if (nums == null || nums.length < 4) return new LinkedList<>();
        
		// 思路2:使用三层循环,外加一个set,类似三数之和(二数之和)
        List<List<Integer>> result = new ArrayList<>();
        Set<Integer> set = new HashSet<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
    
    
            for (int j = i + 1; j < nums.length; j++) {
    
    
                for (int k = j + 1; k < nums.length; k++) {
    
    
                    if (set.contains(target - nums[i] - nums[j] - nums[k])) {
    
    
                        List<Integer> subList = new LinkedList<>();
                        subList.add(target - nums[i] - nums[j] - nums[k]);
                        subList.add(nums[i]);
                        subList.add(nums[j]);
                        subList.add(nums[k]);
                        // 判断是否存在,也就是为了去重
                        if (!result.contains(subList))
                            result.add(subList);
                    }
                }
            }
            set.add(nums[i]);
        }
        return result;
    }
}

Code explanation

This is the same as the sum of three numbers, except that a layer of loop is added to the idea 2 of the sum of three numbers, and the sum of four numbers can be realized.

One thing needs to be reminded, like the sum of three numbers, set must be detected first and then added, the reason is the same as the sum of three numbers idea 2.

It is still the same, first sort the array, and then traverse the three-layer loop. If there is a difference in the set, add it to the sub-set first; at this time, it is necessary to do some de-duplication judgment if (!result.contains(subList)), and add it only if the result does not contain a sub-set.

After the second and third layers of loop traversal are completed, the value of the first layer of loop is added to the set,set.add(nums[i]);

Submitting the code is obviously much faster than the first, but still not fast enough!
insert image description here

Idea 3

It's still the same as the sum of three numbers, using the front and rear pointers to clip in the middle, but with an additional layer of looping.

Central idea:

  • Sort first! Then the two-layer loop + the front and rear pointers move ( the sum below is the value of the first two-layer loop plus the value of the front and rear pointers )
  • The sum is greater than the target, then the rear pointer moves forward (to make the sum smaller);
  • If the sum is less than the target, the front pointer is moved backward (to make the sum larger)

If you don't understand, take a look at the explanation of the sum of three numbers ?

code

class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        if (nums == null || nums.length < 4) return new LinkedList<>();
        
        // 思路3:使用左右指针往中间夹的思路
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
    
    
            if (i > 0 && nums[i] == nums[i - 1]) 
                continue;
            for (int j = i + 1; j < nums.length; j++) {
    
    
                if (j > i + 1 && nums[j] == nums[j - 1]) 
                    continue;
                int start = j + 1;
                int end = nums.length - 1;
                int tempSum = nums[i] + nums[j];
                while (start < end) {
    
    
                    int sum = tempSum + nums[start] + nums[end];
                    if (sum == target) {
    
    
                        result.add(Arrays.asList(nums[i], nums[j], nums[start], nums[end]));
                        // 去重
                        while (start < end && nums[start] == nums[start + 1]) start++;
                        while (start < end && nums[end] == nums[end - 1]) end--;
                        start++;
                        end--;
                    } else if (sum < target) 
                        start++;
                    else 
                        end--;
                }
            }
        }
        return result;
    }
}

Code explanation

Even the code is similar to the sum of three numbers, no more nonsense, and it is easy to understand if you study it yourself.
It's just that the submission found that the time did not meet my expectations (such as more than 90%+ people)!
insert image description here
With doubts, I looked at the discussion forum and found that the fastest is 3ms, I often miss 17ms, I am ashamed! ! !

But after looking at the other party's explanation, sure enough, the idea of ​​3ms is niubility??! Let’s put a picture first, the picture is the explanation of the other party:
insert image description here
Well, the idea is actually the front and rear pointers, but the other party has added some optimizations, which is in the red circle. Combined with the other party’s code, it is easy to understand!
insert image description here
The red circle is the optimized content, that is, the content of my code, let me explain!

In one case, if the sum of the number nums[i] of the first-layer loop and the maximum value, that is, three times the nums[nums.length - 1], is still less than the target, then skip it directly and perform the second loop!

What does that mean?

If the sum of three times the maximum value and the number of loops in the first layer is less than the target value, then the subsequent operations can be done without continuing, and continue;the number of loops in the first layer can be directly increased! It should be easy to understand! The following code is used to solve this situation:

if( nums[i] + 3 * nums[nums.length - 1] < target ) // current num is too small
	continue;

Another situation is that the current number is too large. Think about it, if four times the number of loops in the first layer (that is, the minimum value) is greater than the target, then there is no need to count it later, because the minimum value is Four times are larger than the target, and the sum of the following numbers must be getting bigger and bigger! Just exit the loop break;!

The following code is used to solve this situation:

if( nums[i] * 4 > target ) // current num is too large
    break;

The above two pieces of code are the content of the first red circle, and the author made it very clear;

The second red circle is the same. It is judged that the value is too large or too small. It is precisely because of these lines of code that it has been optimized a lot at once!

Adding the sum of optimizations, my code becomes this:

class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        if (nums == null || nums.length < 4) return new LinkedList<>();
        
        // 思路3:使用左右指针往中间夹的思路
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
    
    
            if (i > 0 && nums[i] == nums[i - 1]) 
                continue;
            if (nums[i] + 3 * nums[nums.length - 1] < target ) // 当前数太小了,无法凑到target,可以直接跳过
                continue;
            if (nums[i] * 4 > target ) // 当前数太大了,这时候由于当前数是数组中的最小值,后面也就没有必要继续探测
                break;
            for (int j = i + 1; j < nums.length; j++) {
    
    
                if (j > i + 1 && nums[j] == nums[j - 1]) 
                    continue;
                if (nums[i] + nums[j] + 2 * nums[nums.length - 1] < target ) // 当前数太小了,无法凑到target,可以直接跳过
                    continue;
                if (nums[i] + nums[j] * 3 > target ) // 当前数太大了,这时候由于当前数是数组中的最小值,后面也就没有必要继续探测
                    break;
                int start = j + 1;
                int end = nums.length - 1;
                int tempSum = nums[i] + nums[j];
                while (start < end) {
    
    
                    int sum = tempSum + nums[start] + nums[end];
                    if (sum == target) {
    
    
                        result.add(Arrays.asList(nums[i], nums[j], nums[start], nums[end]));
                        // 去重
                        while (start < end && nums[start] == nums[start + 1]) start++;
                        while (start < end && nums[end] == nums[end - 1]) end--;
                        start++;
                        end--;
                    } else if (sum < target) 
                        start++;
                    else 
                        end--;
                }
            }
        }
        return result;
    }
}

Yes it got longer, but after committing you will find out! The time has been optimized a lot at once! At least it looks comfortable! niubility??!
insert image description here


Summarize

Remember a sentence, details determine success or failure!

It is precisely because of a small part of the details that the algorithm time has been improved a lot! Many scenes are like this. For example, if there is a small error in the screw on the rocket, it is estimated that the entire rocket will have a problem!

Whether you're doing something or writing code, it's a good idea to think about edge cases, because it's often these edge cases that determine the quality of your code or product!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326153883&siteId=291194637