subject
Source: LeetCode.
Give you a chance n An array of integers nums ,And a target value target . Please find and return the quads that meet all the following conditions and are not repeated [nums[a], nums[b], nums[c], nums[d]] :
Example 1:
Input: nums = [1,0,-1,0,-2,2], target = 0 Output:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
Example 2:
Input: nums = [2,2,2,2,2], target = 8 Output:[[2,2,2,2]]
Tips:
- 1 < = n u m s . l e n g t h < = 200 1 <= nums.length <= 200 1<=nums.length<=200
- − 109 < = n u m s [ i ] < = 109 -109 <= nums[i] <= 109 −109<=nums[i]<=109
- − 109 < = t a r g e t < = 109 -109 <= target <= 109 −109<=target<=109
Next, let's look at the problem-solving ideas:
Idea: sort + double pointer:
This question is related to Sum of three Similarly, you can use the same idea to solve this problem
In order to avoid enumerating to repeated quads, it is necessary to ensure that the elements enumerated in each re cycle are not less than those enumerated in the previous re cycle, and the same elements cannot be enumerated multiple times in the same re cycle;
You can sort the array and follow the following two points during the loop:
- The subscript enumerated in each loop must be greater than the subscript enumerated in the previous loop;
- In the same loop, if the current element is the same as the previous element, the current element is skipped;
Because the array has been sorted, you can use the double pointer method to remove a double loop;
Keep the third loop unchanged, and turn the fourth loop into a pointer moving to the left from the rightmost end of the array;
Note: it is also necessary to keep the left pointer always on the left side of the right pointer (i.e. meet the requirements) c ≤ d c \leq d c≤d)
public static List<List<Integer>> fourSum1(int[] nums, int target) { List<List<Integer>> result = new ArrayList<>(); if (nums == null || nums.length < 4) { return result; } // sort Arrays.sort(nums); int len = nums.length; // Enumerate the first digit a for (int i = 0; i < len; ++i) { // It needs to be different from the last enumeration if (i > 0 && nums[i] == nums[i - 1]) { continue; } // Enumerate the second number b for (int j = i + 1; j < len; ++j) { // It needs to be different from the last enumeration if (j > i + 1 && nums[j] == nums[j - 1]) { continue; } // Enumerate the third digit c for (int k = j + 1; k < len; ++k) { // It needs to be different from the last enumeration if (k > j + 1 && nums[k] == nums[k - 1]) { continue; } // Right pointer int m = len - 1; // It is necessary to ensure that the third number c is to the left of the fourth number d while (m > k && nums[i] + nums[j] + nums[k] + nums[m] > target) { --m; } // The third number c coincides with the fourth number d. with the subsequent increase of c, // If a+b+c+d=0 and c < D is not satisfied, the loop can be exited. if (m == k) { break; } if (nums[i] + nums[j] + nums[k] + nums[m] == target) { List<Integer> list = new ArrayList<>(); list.add(nums[i]); list.add(nums[j]); list.add(nums[k]); list.add(nums[m]); result.add(list); } } } } return result; }
However, even using double pointers saves a layer of for loop, but the efficiency is still not high, and some optimization can be done;
Use the double loop to enumerate the first two numbers respectively, and then use the double pointer to enumerate the remaining two numbers after the number enumerated by the double loop;
Suppose that the first two numbers enumerated by the double loop are in the subscript respectively
i
i
i and
j
j
j. Among them
i
<
j
i<j
I < J. initially, the left and right pointers point to the subscript respectively
j
+
1
j+1
j+1 and subscript
n
−
1
n-1
n − 1. Calculate the sum of four numbers at a time and perform the following operations:
- If sum equals target \textit{target} target, add the four enumerated numbers to the answer, then move the left pointer to the right until different numbers are encountered, and move the right pointer to the left until different numbers are encountered;
- If sum is less than target \textit{target} target, move the left pointer to the right one bit;
- If the sum is greater than target \textit{target} target, move the right pointer one bit to the left.
We can also do some optimization to avoid some redundant loops:
- After determining the first number, if nums [ i ] + nums [ i + 1 ] + nums [ i + 2 ] + nums [ i + 3 ] > target \textit{nums}[i]+\textit{nums}[i+1]+\textit{nums}[i+2]+\textit{nums}[i+3]>\textit{target} Num [i] + num [i + 1] + num [i + 2] + num [i + 3] > target indicates that the sum of the remaining three numbers must be greater than target \textit{target} target, so exit the first loop;
- After determining the first number, if nums [ i ] + nums [ n − 3 ] + nums [ n − 2 ] + nums [ n − 1 ] < target \textit{nums}[i]+\textit{nums}[n-3]+\textit{nums}[n-2]+\textit{nums}[n-1]<\textit{target} nums[i]+nums[n − 3]+nums[n − 2]+nums[n − 1] < target indicates that no matter what value is taken for the remaining three numbers, the sum of the four numbers must be less than target \textit{target} target, so the first loop goes directly to the next round, enumeration nums [ i + 1 ] \textit{nums}[i+1] nums[i+1];
- After determining the first two numbers, if nums [ i ] + nums [ j ] + nums [ j + 1 ] + nums [ j + 2 ] > target \textit{nums}[i]+\textit{nums}[j]+\textit{nums}[j+1]+\textit{nums}[j+2]>\textit{target} Nums [i] + nums [J] + nums [J + 1] + nums [J + 2] > target indicates that no matter what value is taken for the remaining two numbers, the sum of the four numbers must be greater than target \textit{target} target, so exit the second loop;
- After determining the first two numbers, if nums [ i ] + nums [ j ] + nums [ n − 2 ] + nums [ n − 1 ] < target \textit{nums}[i]+\textit{nums}[j]+\textit{nums}[n-2]+\textit{nums}[n-1]<\textit{target} nums[i]+nums[j]+nums[n − 2]+nums[n − 1] < target indicates that the sum of the remaining two numbers must be less than target \textit{target} target, so the second loop goes directly to the next round, enumeration nums [ j + 1 ] \textit{nums}[j+1] nums[j+1];
public static List<List<Integer>> fourSum(int[] nums, int target) { List<List<Integer>> result = new ArrayList<>(); if (nums == null || nums.length < 4) { return result; } Arrays.sort(nums); int n = nums.length; // Enumerate the first digit a for (int i = 0; i < n - 3; ++i) { // It needs to be different from the last enumeration if (i > 0 && nums[i] == nums[i - 1]) { continue; } // At this time, no matter what value is taken for the remaining three numbers, the sum of the four numbers must be greater than the target, so exit the first loop; if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) { break; } // At this time, no matter what value the remaining three numbers take, the sum of the four numbers must be less than the target. Therefore, the first cycle directly enters the next round, enumerating num [i + 1]; if (nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) { continue; } // Enumerate the second number b for (int j = i + 1; j < n - 2; ++j) { // It needs to be different from the last enumeration if (j > i + 1 && nums[j] == nums[j - 1]) { continue; } if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) { break; } if (nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target) { continue; } int left = j + 1; int right = n - 1; while (left < right) { int sum = nums[i] + nums[j] + nums[left] + nums[right]; // If the sum is equal to target, add the four enumerated numbers to the answer, then move the left pointer to the right until different numbers are encountered, and move the right pointer to the left until different numbers are encountered if (sum == target) { List<Integer> list = new ArrayList<>(); list.add(nums[i]); list.add(nums[j]); list.add(nums[left]); list.add(nums[right]); result.add(list); while (left < right && nums[left] == nums[left + 1]) { ++left; } ++left; while (left < right && nums[right] == nums[right - 1]) { --right; } --right; } else if(sum < target){ // If the sum is less than target, the left pointer is shifted one bit to the right ++left; } else { // If the sum is greater than the target, the right pointer is moved one bit to the left --right; } } } } return result; }
summary
Time complexity:
O
(
n
3
)
O(n^3)
O(n3), where
n
n
n is the length of the array. The time complexity of sorting is
O
(
n
log
n
)
O(n \log n)
O(nlogn), the time complexity of enumerating quads is
O
(
n
3
)
O(n^3)
O(n3), so the total time complexity is
O
(
n
3
+
n
log
n
)
=
O
(
n
3
)
O(n^3+n\log n)=O(n^3)
O(n3+nlogn)=O(n3);
Space complexity:
O
(
log
n
)
O(\log n)
O(logn), where
n
n
n is the length of the array. The space complexity mainly depends on the additional space used for sorting. An additional array is used to store the array
nums
\textit{nums}
Copies of nums and sort, with a space complexity of
O
(
n
)
O(n)
O(n);