Leetcode刷题笔记——n-sum问题
2Sum:
题目描述:
给定一个整型数组nums,一个target,从数组中找出a、b,使得a+b=target;并且假定数组中只有一对满足条件的a、b
原始思路:
用两个for循环遍历数组,找出结果,时间复杂度为O(n2)
优化思路:
使用hash表,建立HashMap<Integer, Integer> map,键值对为<nums[i],i>。遍历数组nums,对每个数nums[i],查询hash表中是否有key等于target-nums[i]的记录,若有,则找到;否则,将num[i]和下标i存入hash表。因为hash表的查找时间复杂度可以看作是O(1),所以算法时间复杂度就降为O(n)了。
3Sum:
题目描述:
给定一个整型数组nums,从数组中找出所有不重复的[a,b,c],使得a+b+c=0
原始思路:
3个for循环,但是超时了
优化思路:
使用双指针。首先将数组排序(防止重复),然后遍历数组,在每个位置i,设置双指针,左指针指向位置i+1,右指针指向位置nums.length-1,计算三个位置数之和,若等于0,则将三数位置下标加入结果集,左指针右移,右指针左移;若大于0,则右指针左移;若小于0,则左指针右移,直到左右指针相遇。遍历完数组,找到所有符合条件的[a,b,c]。为防止重复结果,若指针移动的下一个位置上的数与上一个位置上的数字相等,继续移动一位。这种方法的时间复杂度可以控制在O(n2)。
3SumClosest:
题目描述:
给定一个整型数组nums,一个target,从数组中找出3个数之和sum,使得sum与target差的绝对值最小
思路:
与3Sum类似,使用双指针。
4Sum:
题目描述:
给定一个整型数组nums,一个target,从数组中找出所有不重复的四元数组[a, b, c, d],使得a+b+c+d = target
思路:
分解,将4Sum分解成3Sum再分解成2Sum,2Sum可以使用Hashmap或者双指针,从而将时间复杂度控制在O(n3)。实际代码实现中要注意避免重复的问题,并且可以使用一些判断条件(如:因为数组为升序,如果当前nums[i]*4 > target,就可以直接break了)进行速度优化。
具体实现:
public List<List<Integer>> fourSum(int[] nums, int target){
List<List<Integer>> res = new ArrayList<>();
// 数组为空或长度小于4,返回
if(nums == null || nums.length < 4)
return res;
//排序
Arrays.sort(nums);
//数组太大或数组太小,返回
if(nums[0]*4 > target || nums[nums.length-1]*4 < target) return res;
for(int i=0; i<nums.length-3; i++){
//注意判断条件不能是nums[i+1] == nums[i],因为单组结果中可以出现重复的数字
if(i != 0 && nums[i-1] == nums[i]) continue; //避免重复
if(nums[i]*4 == target){
if(nums[i+3] == nums[i]){
res.add(Arrays.asList(nums[i], nums[i], nums[i], nums[i]));
return res;
}
}
if(nums[i] + 3*nums[nums.length-1] < target) continue; //nums[i]太小
if(nums[i] * 4 > target) return res; //nums[i]太大
threeSumForFourSum(nums, target-nums[i], i+1, res, nums[i]);
}
return res;
}
public void threeSumForFourSum(int[] nums, int target, int start, List<List<Integer>> res, int z1){
if(3*nums[start] > target || 3*nums[nums.length-1] < target) return;
for(int i=start; i<nums.length-2; i++){
if(nums[i]*3 > target) return; //过大
if(nums[i] + nums[nums.length-1]*2 < target) continue; //过小
if(i != start && nums[i-1] == nums[i]) continue; //避免重复
if(3*nums[i] == target){
if(nums[i+2] == nums[i]){
res.add(Arrays.asList(z1,nums[i],nums[i],nums[i]));
return;
}
}
twoSumForFourSum(nums, target-nums[i], i+1, res, z1, nums[i]);
}
}
public void twoSumForFourSum(int[] nums, int target, int start, List<List<Integer>> res, int z1, int z2){
if(start == nums.length-1) return;
if(nums[start] *2 > target) return;
HashMap<Integer, Integer> map = new HashMap<>();
for(int i= start; i<nums.length; i++){
if(i != start && nums[i-1] == nums[i]) continue; //避免重复
if(i!= nums.length-1 && 2*nums[i] == target){
if(nums[i+1] == nums[i]){
res.add(Arrays.asList(z1,z2,nums[i],nums[i]));
}
}
int cut = target-nums[i];
if(map.containsKey(cut)){
res.add(Arrays.asList(z1,z2,cut,nums[i]));
continue;
}
map.put(nums[i],i);
}
}