给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [], target = 0
输出:[]
提示:
0 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
双指针解法
/**
* 18. 四数之和
* 1. ArrayList的实现是基于数组,LinkedList的实现是基于双向链表。
* 2. 对于随机访问,ArrayList优于LinkedList,ArrayList可以根据下标以O(1)时间复杂度对元素进行随机访问。
* 而LinkedList的每一个元素都依靠地址指针和它后一个元素连接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)
* 3. 对于插入和删除操作,LinkedList优于ArrayList,因为当元素被添加到LinkedList任意位置的时候,
* 不需要像ArrayList那样重新计算大小或者是更新索引。
* 4. LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,
* 一个指向前一个元素,一个指向后一个元素。
*
*
* asList 将数组转化成List
* @param nums
* @param target
* @return
*/
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> lists = new ArrayList<>();
if (nums.length == 0 || nums == null) {
return lists;
}
//对数组中的数据进行排序
Arrays.sort(nums);
//获取数组长度
int length = nums.length;
//定义四个指针k,i,left,right k从0开始遍历 i从k+1开始遍历 留下left和right left指向i+1 right指向此时的最大值
//为何length-3呢? 由于同时存在四个指针,每一个指针都会占据一个位置 当length-3的时候 就只剩下一种情况了
//i=length-2 left=length-1 right=length
for (int k = 0; k < length - 3; k++) {
//如果在k>0的基础上 nums[k]==nums[k-1] 只能表明两个数字是一样 那么它和k-1循环的结果相差不是很大,甚至有可能一样
//因此没有必要 可以进行忽略
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
//由于此数组已经进行过排序 获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏
int min1 = nums[k] + nums[k + 1] + nums[k + 2] + nums[k + 3];
if (min1 > target) {
break;
}
//由于此数组已经进行过排序 获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略
int max1 = nums[k] + nums[length - 1] + nums[length - 2] + nums[length - 3];
if (max1 < target) {
continue;
}
//第二层循环i,初始值指向k+1 由于k值相当于占取了一位,第二位就没必要去取第一位的数据的,每次数据只能出现一次
for (int i = k + 1; i < length - 2; i++) {
//第二层的方法以下内推
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
//注意此时的首位已经确定
int min2 = nums[k] + nums[i] + nums[i + 1] + nums[i + 2];
if (min2 > target) {
break;
}
int max2 = nums[k] + nums[i] + nums[length - 1] + nums[length - 2];
if (max2 < target) {
continue;
}
//定义指针j指向i+1
int left = i + 1;
//定义指针h指向数组末尾
int right = length - 1;
while (left < right) {
int num = nums[k] + nums[i] + nums[left] + nums[right];
if (num < target) {
left++;
} else if (num > target) {
right--;
} else {
//存储对应的数据,接着继续向下寻找 直到查找结束
lists.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));
left++;
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
right--;
while (left < right && i < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
}
return lists;
}