|
11 | 11 | */
|
12 | 12 | class Solution {
|
13 | 13 | /**
|
14 | | - * 方法一:二分查找 |
15 | | - * |
16 | | - * • 时间复杂度:O(N^2logN)。枚举 i 和 j 各需要 O(N) 的时间复杂度,二分查找 k 需要 O(logN) 的时间复 |
17 | | - * 杂度,因此总的时间复杂度为 O(N^2logN)。 |
18 | | - * • 空间复杂度:O(1)。 |
| 14 | + * 方法:双指针 |
19 | 15 | */
|
20 | | - public int threeSumSmaller1(int[] nums, int target) { |
| 16 | + public int threeSumSmaller(int[] nums, int target) { |
21 | 17 | /*
|
22 | | - * 首先考虑更简单的情况。题目中要求的是三元组 (i,j,k),可以首先尝试一个简单的版本,求出二元 |
23 | | - * 组 (i,j)。即 |
24 | | - * 给定一个长度为 n 的数组 nums,找出所有的二元组 (i,j) 的数目,满足 0≤i<j<n 且 nums[i]+ |
25 | | - * nums[j]<target。 |
| 18 | + * 首先考虑更简单的情况。题目中要求的是三元组 (i,j,k),可以首先尝试一个简单的版本,求出二元组 (i,j)。 |
26 | 19 | *
|
27 | | - * 在这个简化的问题中,首先对数组进行排序,随后从小到大枚举 i,并用二分查找,找出最大的满足 |
28 | | - * nums[i]+nums[j]<target 的 j,那么就可以知道对于当前的 i,有 j−i 对二元组是满足要求的。 |
29 | | - * |
30 | | - * 解决了二元组的问题后,可以发现,在问题变成三元组时,只要在二元组的算法框架外部再套上一层循 |
31 | | - * 环就行了。也就是说,从小到大枚举 i 和 j,规定 i<j,随后在数组上二分查找,找出最大的满足 |
32 | | - * nums[i]+nums[j]+nums[k]<target 的 k,那么对于当前的 i 和 j,有 k−j 对三元组是满足要求的。 |
33 | | - */ |
34 | | - Arrays.sort(nums); |
35 | | - int sum = 0; |
36 | | - for (int i = 0; i < nums.length - 2; ++i) { |
37 | | - sum += twoSumSmaller1(nums, i + 1, target - nums[i]); |
38 | | - } |
39 | | - return sum; |
40 | | - } |
41 | | - |
42 | | - private int twoSumSmaller1(int[] nums, int startIndex, int target) { |
43 | | - int sum = 0; |
44 | | - for (int i = startIndex; i < nums.length - 1; ++i) { |
45 | | - int j = binarySearch(nums, i, target - nums[i]); |
46 | | - sum += j - i; |
47 | | - } |
48 | | - return sum; |
49 | | - } |
50 | | - |
51 | | - private int binarySearch(int[] nums, int startIndex, int target) { |
52 | | - int left = startIndex; |
53 | | - int right = nums.length - 1; |
54 | | - while (left < right) { |
55 | | - int mid = (left + right + 1) / 2; |
56 | | - if (nums[mid] < target) { |
57 | | - left = mid; |
58 | | - } else { |
59 | | - right = mid - 1; |
60 | | - } |
61 | | - } |
62 | | - return left; |
63 | | - } |
64 | | - |
65 | | - /** |
66 | | - * 方法二:双指针 |
67 | | - * |
68 | | - * • 时间复杂度:O(N^2)。在每一步中,要么 left 向右移动一位,要么 right 向左移动一位。当 left≥right |
69 | | - * 时循环结束,因此它的时间复杂度为 O(N)。加上外层的循环,总的时间复杂度为 O(N^2)。 |
70 | | - * • 空间复杂度:O(1)。 |
71 | | - */ |
72 | | - public int threeSumSmaller2(int[] nums, int target) { |
73 | | - /* |
74 | | - * 在方法一中,首先解决了一个简化版的问题,随后在外部套上一层循环,就可以解决当前的问题。如果 |
75 | | - * 能找出更好的解决简化版的问题的方法,就能在更低的时间复杂度内解决当前的问题。 |
76 | | - * |
77 | | - * 首先仍然对数组进行排序,例如 nums=[3,5,2,8,1] 排序后变成 [1,2,3,5,8]。target 的值为 7。 |
| 20 | + * 首先对数组进行排序,例如 nums=[3,5,2,8,1] 排序后变成 [1,2,3,5,8]。target 的值为 7。 |
78 | 21 | * [1, 2, 3, 5, 8]
|
79 | 22 | * ↑ ↑
|
80 | 23 | * left right
|
| 24 | + * |
81 | 25 | * 用两个指针 left 和 right 分别指向数组中的第一个和最后一个元素,它们的和为 1+8=9>target,这
|
82 | 26 | * 说明 right 不能出现在二元组中(因为 left 只向左移动,如果此时二者的和已经大于 target,那么在 left 移
|
83 | 27 | * 动的过程中,二者的和就不可能小于 target 了),因此需要将 right 向左移动一位。
|
84 | 28 | * [1, 2, 3, 5, 8]
|
85 | 29 | * ↑ ↑
|
86 | 30 | * left right
|
| 31 | + * |
87 | 32 | * 现在二者的和为 1+5=6<target,那么对于当前的 left,应当有 right−left=3 对二元组满足要求,它们
|
88 | 33 | * 分别为 (1,2),(1,3),(1,5)。在这之后,将 left 向右移动一位。
|
89 | 34 | */
|
90 | 35 | Arrays.sort(nums);
|
91 | 36 | int sum = 0;
|
92 | 37 | for (int i = 0; i < nums.length - 2; i++) {
|
93 | | - sum += twoSumSmaller2(nums, i + 1, target - nums[i]); |
| 38 | + sum += twoSumSmaller(nums, i + 1, target - nums[i]); |
94 | 39 | }
|
95 | 40 | return sum;
|
96 | 41 | }
|
97 | 42 |
|
98 | | - private int twoSumSmaller2(int[] nums, int startIndex, int target) { |
| 43 | + private int twoSumSmaller(int[] nums, int startIndex, int target) { |
99 | 44 | int sum = 0;
|
100 | 45 | int left = startIndex;
|
101 | 46 | int right = nums.length - 1;
|
102 | 47 | while (left < right) {
|
103 | 48 | if (nums[left] + nums[right] < target) {
|
104 | 49 | sum += right - left;
|
105 | | - left++; |
| 50 | + ++left; |
106 | 51 | } else {
|
107 | | - right--; |
| 52 | + --right; |
108 | 53 | }
|
109 | 54 | }
|
110 | 55 | return sum;
|
111 | 56 | }
|
112 | 57 |
|
113 | 58 | public static void main(String[] args) {
|
114 | | - System.out.println(new Solution().threeSumSmaller1(new int[]{-2, 0, 1, 3}, 2)); |
115 | | - System.out.println(new Solution().threeSumSmaller2(new int[]{-2, 0, 1, 3}, 2)); |
| 59 | + System.out.println(new Solution().threeSumSmaller(new int[]{-2, 0, 1, 3}, 2)); |
116 | 60 | }
|
117 | 61 | }
|
0 commit comments