[leetcode]_K Sum 问题
问题:K Sum问题是一个问题系列,在一个数组中找K个数的和能够满足题目中要求。从2 Sum 到 3 Sum , 3 Sum Clozet , 4 Sum。。解法虽一开始不容易想到,但get到解题技能后,该系列的题目其实解法较为单一。
一、核心解题思路。Two Sum。
题目:一个数组a中,找寻两个数,使其和等于target。返回两个数的下标。
思路:最白目的思路是O(n2)解法,无需多言。精彩的解法能够O(n)完成算法。
将该数组进行排序。设置两个指针start,end指向数组的头尾。如果a[start] + a[end] < target,那么将start指针往后移,因为当前的和值比目标值小;反之,将end指针向前移,因为当前的和值比目标值大。直到两个指针相遇,结束查找。
代码:由于要返回两个数的下标,因此在对数组进行排序之前,需要记录数据的原始下标。
1 public class Solution { 2 public int[] twoSum(int[] numbers, int target) { 3 4 if(numbers.length <= 1) return null; 5 6 Pair[] pairs = new Pair[numbers.length]; 7 for(int i = 0 ; i < numbers.length ; i++){ 8 pairs[i] = new Pair(numbers[i] , i+1); 9 } 10 11 Comparator<Pair> comparator = new Comparator<Pair>(){ 12 public int compare(Pair o1 , Pair o2){ 13 return o1.val > o2.val ? 1 : -1 ; 14 } 15 }; 16 17 Arrays.sort(pairs , comparator); 18 19 int index1 = 0 , index2 = numbers.length - 1; 20 21 while(index1 < index2){ 22 int temp = pairs[index1].val + pairs[index2].val; 23 if(temp == target) break; 24 else if(temp < target) index1++; 25 else index2--; 26 } 27 if(pairs[index1].index < pairs[index2].index) return new int[]{pairs[index1].index , pairs[index2].index}; 28 else return new int[]{pairs[index2].index , pairs[index1].index}; 29 } 30 } 31 32 class Pair{ 33 int val; 34 int index; 35 Pair(int x , int y){ 36 val = x; 37 index = y; 38 } 39 }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
二、举一反三。3 Sum。
问题:一个数组a,判断其中是否存在三个数a , b , c 使其和为target。返回这些三元组集合。
思路:该问题可退化为 2 Sum问题来解答。先选择任意一个数a ,然后判断剩余数组中是否存在另外两个数的和等于target - a。<----这里就完全是2 Sum问题的方法了。
代码:
1 public ArrayList<ArrayList<Integer>> threeSum(int[] num) { 2 3 ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); 4 5 Arrays.sort(num); 6 7 for(int i = 0 ; i < num.length ; i++){ 8 int target = 0 - num[i]; 9 //由于结果要求三元组的值要非递减顺序,因此将数组排序后,从头开始定第一个数据,后两个数据从该数据的后部选取。 10 for(int j = i + 1 , k = num.length - 1 ; j < num.length && k >= 0 && j < k ;){ 11 int temp = num[j] + num[k]; 12 if(temp == target){ 13 14 boolean ifAdd = true; 15 16 //judge duplicate 17 for(int index = 0 ; index < result.size() ; index++){ 18 if(result.get(index).get(0) == num[i] 19 && result.get(index).get(1) == num[j] 20 && result.get(index).get(2) == num[k]){ 21 ifAdd = false; 22 break; 23 } 24 } 25 if(ifAdd){ 26 ArrayList<Integer> one = new ArrayList<Integer>(); 27 one.add(num[i]); 28 one.add(num[j]); 29 one.add(num[k]); 30 result.add(one); 31 } 32 j++; 33 34 }else if(temp < target) j++; 35 else k--; 36 } 37 } 38 return result; 39 }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
三、练习熟悉。3 Sum Closest。
题目:给顶一个数组a,求最接近target的三元组的和。输出该和。
思路:完全与3 Sum问题相同。唯一不同的地方在于,因为它是判断与target最接近,因此利用绝对值来判断当前和是否与target最接近。
代码:
public int threeSumClosest(int[] num, int target) { Arrays.sort(num); int min = Integer.MAX_VALUE; for(int i = 0 ; i < num.length ; i++){ for(int j = i + 1 , k = num.length - 1 ; j < num.length && k >= 0 && j < k ;){ int threeSum = num[j] + num[k] + num[i]; if(threeSum == target) { min = 0; break; }else{ int dis = target - threeSum; //利用绝对值来判断是否与traget最接近。 if(Math.abs(dis) < Math.abs(min)) { min = dis; } if(dis > 0) j++; else k--; } } if(min == 0){ break; } } return target - min; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
四、还是练习。4 Sum。
问题:一个数组a,寻找是否存在四个数a + b + c + d = target。返回四元组集合。
思路:先固定住一个数a , 然后寻找剩余数组中是否存在三个数和 等于 target - a(退化为3Sum),然后计算三个数和时,再先固定一个数b,然后寻找剩余数组中是否存在两个数和等于target-a -b(退化为2Sum)。
代码:
1 public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) { 2 3 ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); 4 5 if(num.length <= 3) return result; 6 7 Arrays.sort(num); 8 9 for(int i = 0 ; i <= num.length - 4 ; i++){ 10 for(int j = i + 1 ; j <= num.length - 3 ; j++){ 11 int start = j + 1; 12 int end = num.length - 1; 13 while(start < end){ 14 int temp = num[i] + num[j] + num[start] + num[end]; 15 if(temp == target){ 16 boolean ifAdd = true; 17 for(int k = 0 ; k < result.size() ; k++){ 18 if(result.get(k).get(0) == num[i] && result.get(k).get(1) == num[j] 19 && result.get(k).get(2) == num[start] && result.get(k).get(3) == num[end]){ 20 ifAdd = false; 21 break; 22 } 23 } 24 25 if(ifAdd){ 26 ArrayList<Integer> each = new ArrayList<Integer>(); 27 each.add(num[i]); 28 each.add(num[j]); 29 each.add(num[start]); 30 each.add(num[end]); 31 result.add(each); 32 } 33 34 start++; 35 36 }else if(temp < target) start++; 37 else end--; 38 } 39 } 40 } 41 42 return result; 43 }
因此,总结K Sum的问题,其核心思路就是2Sum问题,任何K > 2时,都可通过逐层退化,到2Sum。而2Sum问题,在将数据进行排序后,就可通过两个指针来达到要求。