algs4-1.4.14: 我的 O(N^3 * logN)解法 与 GPT4 的 O(N^3) 解法
1.4.14 4-sum。为4-sum设计一个算法。
写到最前: 后续会减少直接贴 GPT 图.
为了便于后续的阅读者和我自己, 方便地直接使用或编辑 GPT 相关的描述, 后续将以引用方式备注出来来自 GPT 的文案或者代码. 直接贴图看着更直观, 但是真到需要用时, 会很尴尬.
我的初版答案, 时间复杂度 O(N^3 * logN)
public class FourSumFast {
public static int count(int[] a) {
Arrays.sort(a);
int N = a.length;
int cnt = 0;
for (int i = 0; i < N; i++) {
for (int j = i + 1; j < N; j++) {
for (int k = j + 1; k < N; k++) {
if (BinarySearch.rank(-a[i] - a[j] - a[k], a) > k) {
cnt++;
}
}
}
}
return cnt;
}
}
思路,相对很简单, 就是把最内层,由循环改成二分查找.如果用四重循环的话, 时间复杂度, 会变成 O(N^4).
给定的用例数据的实行时间如下:
* 1Kints time:3.565 s;count: 13654
* 2Kints time:34.223 s;count: 220115
GPT4 的答案, 基于双指针, 时间复杂度 O(N^3)
public class FourSumFastGPT {
public static int count(int[] nums) {
int target = 0;
int count = 0;
int n = nums.length;
if (nums == null || n < 4)
return count;
Arrays.sort(nums);
for (int i = 0; i < n - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) // Prevents duplicate results
continue;
if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) // min > target
break;
for (int j = i + 1; j < n - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) // Prevents duplicate results
continue;
int left = j + 1, right = n - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
count++;
while (left < right && nums[left] == nums[left + 1])
left++; // Prevents duplications
while (right > left && nums[right] == nums[right - 1])
right--; // Prevents duplications
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return count;
}
}
这个方法, 我确实没想到. 双指针法, 最坏情况下的时间复杂度为 O(N), 再加上两层循环, 所以就是 O(N^3).
下面是 GPT4的解题思路:
算法思想
对于四数之和的问题,一个有效的解决方案是固定两个数,然后使用双指针对剩下的两个数进行处理. 为了使问题更简单,首先对数组进行排序.
此外,考虑到算法需要找出所有满足条件的四元组, 而不仅仅是计算有多少个这样的四元组,所以需要避免重复。
步骤
-
对数组
a[]
进行排序。 -
用两个嵌套循环在
a[]
中固定两个元素a[i]
和a[j]
。 -
在
i+1
和j-1
之间用两个指针k
和m
寻找满足a[i] + a[j] + a[k] + a[m] = 0
的元素对。-
如果总和大于0,那么
m--
。 -
如果总和小于0,那么
k++
。 -
如果总和等于0,那么这四个元素就是一个解。
-
-
跳过所有重复的元素对,以避免重复的结果。
浅思: 这是第二次碰到 双指针法
需要适当 双指针法. 目前看, 抽象方法, 主要是将对数组的操作, 转换为对指针的操作. 配合对数据进行排序, 可以兼容一些冗余的非必要操作, 如: 不用反复地来回遍历数组.