K-Sum问题总结,包括2-Sum、3-Sum、3-SumClosest和4-Sum

     关于k_sum问题,在leetcode刷题的过程中,遇到了。做完同类型的题目之后,在这里总结一下:

一、2-Sum

  1. 问题描述

* Given an array of integers, return indices of the two numbers such that they add up to a specific target.
* You may assume that each input would have exactly one solution, and you may not use the same element twice.
* Example: Given nums = [2, 7, 11, 15], target = 9,
* Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
* If the result above does not exist,return null;

  2. 暴力解的方法

// 方法1:使用双层循环,直到找到 和 是target的两个数为止:
// 时间复杂度 O(n^2)
public static int[] twoSum_1(int[] nums, int target) {
   int [] res = new int[2];   //存放结果的数组res
   for(int i = 0; i < nums.length - 1;i++) {
      for(int j = i + 1;j < nums.length;j++) {
         if(nums[i] + nums[j] == target) {
            res[0] = i;
            res[1] = j;
            return res;
         }
      }
   }
   return null;
}

  3. 采用HashMap

// 方法2:进行一遍循环,当遍历到nums[i]时,看target-nums[i]这个key在不在hashmap中。
// 如果不在就如果在就把nums[i](key) ------ i(value)  放在map中
// 在的话,就获取其value,以及当前遍历到的nums[i]中的下标i值,作为返回结果。
// 时间复杂度 O(n)
public static int[] twoSum_2(int[] nums, int target) {
   int [] res = new int[2];
   HashMap<Integer, Integer> hm = new HashMap<>();
   for(int i = 0;i < nums.length;i++) {
      //这里要注意使用containsKey函数,而不是containsValue
      if(hm.containsKey(target - nums[i])) {
         res[0] = hm.get(target - nums[i]);
         res[1] = i;
         return res;
      }
      else
         hm.put(nums[i], i);
   }
   return null;
}

4.采用排好了序的数组的时候,时间复杂度可以优化为O(logn)

//因为是排好了序的,所以可以使用二分查找,时间复杂度O(log n)
public static int[] twoSum_4(int[] numbers, int target) {
	int [] res = new int[2];
	int start = 0, end = numbers.length - 1;
	while(start < end){
		if(numbers[start] + numbers[end] == target){
			res[0] = start + 1;
			res[1] = end + 1;
			break;
		}
		else if(numbers[start] + numbers[end] < target)
			start ++;
		else
			end --;
	}
	return res;
}

 5. 测试程序

//测试程序
public static void main(String[] args) {
   System.out.println("1: two_sum 测试:");
   int [] a = {3, 2, 4};
   int [] r = twoSum_2(a, 6);
   if(r == null)
      System.out.println("不存在");
   else
      System.out.println(r[0] + " " + r[1]);
}

二、3-Sum

   1. 问题描述

* Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? 
* Find all unique triplets in the array which gives the sum of zero.
* Note: The solution set must not contain duplicate triplets.
* Example: Given array nums = [-1, 0, 1, 2, -1, -4], the solution: [ [-1, 0, 1], [-1, -1, 2] ]
* if no solution exist, return null.

  2. 暴力解方法

* 其实就是遍历这里面的每个数,做三次循环。时间复杂度为 O(n^3).
* 同时,还要考虑去除掉重复出现的set,所以这里就不实现了。

  3. 延续之前2-Sum的HashMap的方法

/*
 * 方法:首先,遍历一次数组,将其值(key)和下标(value)存放到map中。
 * 然后只进行两层循环,求出nums[i] + nums[j]的值,看这个值的相反数在不在map中
 * 如果在的话,就获得其value,也就是下标。然后返回。
 * 这种的时间复杂度是(n^2) 在Leetcode中超出了运行时间
 */
public static List<List<Integer>> threeSum_1(int[] nums) {
   //生成一个存放列表(子列表中放的是整数对象)的列表
   int len = nums.length;   //获取数组的长度
   if(len < 3)
      return null;
   HashMap<Integer, Integer> hashMap = new HashMap<>();
   List<List<Integer>> list = new ArrayList<>();       //存放返回结果
   for(int i = 0; i < len; i ++){
      hashMap.put(nums[i], i);
   }
   for(int j = 0;j < len;j++)
   {
      for(int k = j + 1;k < len;k++){
         int sum = nums[j] + nums[k];
         if(hashMap.containsKey(-sum)) {
            //这里要特别注意排除重复的下标
            int i = hashMap.get(-sum);
            if(i != j && i != k) {
               List<Integer> list1 = Arrays.asList(nums[i], nums[j], nums[k]);
               list1.sort(null);
               if(!list.contains(list1))
                  list.add(list1);
            }
         }
      }
   }
   return list;
}

 

  4. 采用排好序的数组,然后选中一个值,再用两边向中间夹的方法,去找这个值得相反数

 

/*
 * 办法:先对nums数组进行一次排序:-4 -1 -1 0 1 2
 * 然后,进行一次循环,将第一个节点取为nums[i],然后从后面部分找和为-nums[i]的两个数
 * 由于是排好了序的数组,可以设置j = i+1,k = len-1
 * 这样nums[j]与nums[k]的和就是一个不大不小的数。
 * 如果这个和比-nums[i]大了,就把k向左移;反之,就把j往右移。
 * 相等的话,就可以把nums[i],nums[j],nums[k]加入res中了.
 * 时间复杂度是 O(n^2)
 */
public static List<List<Integer>> threeSum_2(int [] nums){
   int len = nums.length;
   List<List<Integer>> res = new ArrayList<>();
   Arrays.sort(nums);
   for(int i = 0;i < len; i ++){
      if(i > 0 && nums[i] == nums[i - 1])
         continue;
      int j = i + 1;
      int k = len - 1;
      while(j < k){
         int sum = nums[j] + nums[k];
         if(sum> -nums[i])
            k --;
         else if(sum < -nums[i])
            j ++;
         else{
            res.add(Arrays.asList(nums[i], nums[j], nums[k]));
            //把两个指针先移动,再判断是不是与之前的还是重复
            j ++;
            k --;
            while(j < k && nums[j] == nums[j-1]) j++;
            while(j < k && nums[k] == nums[k+1]) k--;
         }
      }
   }
   return res;
}

  5. 测试程序

public static void main(String[] args) {
   System.out.println("test: ");
   int [] nums = {-4, -1, -1, 0, 1, 2};
   List<List<Integer>> list = threeSum_2(nums) ;
   System.out.println("list = " + list);
}

三、3-Sum closest

  1. 问题描述

Given an array nums of n integers and an integer target, 
find three integers in nums such that the sum is closest to target, return the sum of the three integers. 
You may assume that each input would have exactly one solution.
Example: Given array nums = [-1, 2, 1, -4], and target = 1.
You should return 2 becase: -1 + 2 + 1 = 2

  2. 采用3-sum问题的解决思路,只是这里不在比较相等,而是比较三数之和与target的差值谁更小一点。

 

//时间复杂度: O(n^2)
public static int threeSumClosest(int[] nums, int target) {
   int len = nums.length;
   if(len < 3)
      return 0;
   Arrays.sort(nums);
   int res = nums[0] + nums[1] + nums[2];                   //作为差值,初始化为头三个数据的和
   for(int i = 0;i < len;i++){
      int j = i + 1;
      int k = len - 1;
      while(j < k){
         int sum = nums[i] + nums[j] + nums[k];            //求当前三个数的和
         //使sum逐渐接近target
         if(res > target)
            k--;
         else
            j ++;
         //每次都需要看是否应该更新res的值  
         if(Math.abs(res - target) > Math.abs(res - target))
            res = sum;
      }
   }
   return res;
}

  3. 测试程序

public static void main(String[] args) {
  System.out.println("test: ");
  int [] nums = {-1, 2, 1, -4};
  int target = 1;
  System.out.println("the closest sum: " + threeSumClosest(nums, target));
}

四、4-Sum

  1. 问题描述

Given an array nums of n integers and an integer target, 
are there elements a, b, c, and d in nums such that a + b + c + d = target? 
Find all unique quadruplets in the array which gives the sum of target.
Example: Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:[ [-1,  0, 0, 1], [-2, -1, 1, 2], [-2,  0, 0, 2]]
if no solution exists, return null.

  2. 解法:转换成为很多个3-Sum问题,然后调用之前的方法

 

// 时间复杂度 O(N^3)
public static List<List<Integer>> fourSum(int[] nums, int target) {
   int len = nums.length;
   Arrays.sort(nums);   //从小到大对nums进行排序
   List<List<Integer>> res = new ArrayList<>();
   // 排除nums长度不足4、最小的四个数之和大于target、最大的四个数之和小于target这三种情况。
   if(len < 4)
      return res;
   else if(nums[0] + nums[1] + nums[2] + nums[3] > target || nums[len - 4] + nums[len - 3] + nums[len - 2] + nums[len - 1] < target)
      return res;  //一定是4个数的和,不要写漏了
   else {
      //从后面剩余的数据中找 和为target - num[i] 的元素
      for(int i = 0;i < len - 3;i++) {
         //这里依旧要跳过重复的数值
         if(i > 0 && nums[i] == nums[i - 1])
            continue;
         res.addAll(threeSum(nums, target - nums[i], i + 1));  //方便起见,传整个数组,但通过i+1来限制开始查找的位置
      }
   }
   return res;
}

//这里的target是3-Sum问题中的目标值,start是开始位置
public static List<List<Integer>> threeSum(int [] nums, int target, int start){
   int len = nums.length;
   List<List<Integer>> res = new ArrayList<>();
   //Arrays.sort(nums);这里无须再次排序
   for(int i = start;i < len - 2; i ++){
      //注意: 这里要更新开始的位置,不再是之前的0,而是新的start
      if(i > start && nums[i] == nums[i - 1])
         continue;
      int j = i + 1;
      int k = len - 1;
      while(j < k){
         int sum = nums[j] + nums[k];
         if(sum> target - nums[i])
            k --;
         else if(sum < target - nums[i])
            j ++;
         else{
            //这里要补充上之前在调用的时候那个当前值
            res.add(Arrays.asList(nums[start - 1], nums[i], nums[j], nums[k]));
            while(j < k && nums[j] == nums[j+1]) j++;
            while(j < k && nums[k] == nums[k-1]) k--;
            j ++;
            k --;
         }
      }
   }
   return res;
}

3. 测试程序

public static void main(String[] args) {
   System.out.println("test: ");
   int [] nums = {-3,-2,-1,0,0,1,2,3};
   int target = 5;
   List<List<Integer>> list = fourSum(nums, target);
   System.out.println("list = " + list);
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值