题目:
分析:
解法一:暴力O(n^2)时间复杂度求解。
/** * 解法1:时间复杂度O(n^2),空间复杂度O(1) * 遍历求两数之和等于target,返回两数下标(从1开始) * http://www.lintcode.com/zh-cn/problem/two-sum/ * @author yzwall */ class Solution { public int[] twoSum(int[] nums, int target) { int[] results = new int[2]; for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] + nums[j] == target) { results[0] = i + 1; results[1] = j + 1; return results; } } } return results; } }
解法二:双指针O(nlog(n))时间复杂度求解
思路:首先保存数组排序前的元素位置,可以使用hashmap保存(空间复杂度为O(n)),然后将数组排序(时间复杂度为O(nlogn),然后通过双指针分别指向排序后的数组头和尾,两端同时遍历,如果两指针数组元素之和小于target,则头指针往后移,若之和大于target,则尾指针往前移,若之和正好等于target,则输出下标位置即可。注意,可能存在数组中有相同元素的情况,即对应于hashmap中同key值下有多value的情况,用list保存,故在取出时,应同时删除对应list中的记录。同时要保证第一个下标小于第二个下标。
复杂度分析:时间复杂度O(nlog(n)),空间复杂度O(n).
//解法一:双指针O(nlog(n))时间复杂度求解 public int[] twoSum(int[] numbers, int target) { // write your code here if(numbers.length==0) return null; int[] num=new int[2]; //通过hashmap记录数组排序前的下标 HashMap<Integer,ArrayList<Integer>> hashMap=new HashMap<>(); for(int i=0;i<numbers.length;i++){ if(hashMap.containsKey(numbers[i])){ hashMap.get(numbers[i]).add(i); }else{ hashMap.put(numbers[i],new ArrayList<>()); hashMap.get(numbers[i]).add(i); } } Arrays.sort(numbers); //排序 int low=0; int high=numbers.length-1; while(low<high){ int sum=numbers[low]+numbers[high]; if(sum==target){ int index1=hashMap.get(numbers[low]).get(0); //把上一步取出的值从hashmap中去掉,防止数组中存在相同元素的情况,这种情况下不去除掉就会取得同样的下标值 hashMap.get(numbers[low]).remove(0); int index2=hashMap.get(numbers[high]).get(0); //保证index1<index2; num[0]=Math.min(index1,index2); num[1]=Math.max(index1,index2); return num; }else if(sum>target){ high--; }else{ low++; } } return num; }
解法三:HashMap的O(n)时间复杂度求解
思路:耗费O(n)空间构造哈希表,遍历数组每个元素nums[i],哈希表对应存储<target-nums[i],i>,存储nums[i]期望的另一半,一旦哈希表中包含nums[i],代表“另一半”已经存储在哈希表中,直接返回即可。
复杂度分析:时间复杂度O(n),空间复杂度O(n).
//解法二:HashMap O(n)时间复杂度求解 public int[] twoSum1(int[] numbers, int target) { if(numbers.length==0) return null; int[] num=new int[2]; HashMap<Integer,Integer> hashMap=new HashMap<>(); for(int i=0;i<numbers.length;i++){ if(hashMap.containsKey(numbers[i])){ int index1=hashMap.get(numbers[i]); int index2=i; //保证index1<index2; num[0]=Math.min(index1,index2); num[1]=Math.max(index1,index2); return num; }else{ hashMap.put(target-numbers[i],i); } } return num; }
题目:
分析:这题只需要返回三个整数即可,不需要返回下标,所以直接排序即可。整个题目的解题思路同二数之和差不多。通过控制一个指针i不变,其余两个指针分别指向j和r分别指向i+1和数组最后一个位置,也就是双指针的方法,target为0-指针i指向的值。算法如下:
public class threeSum { /** * @param numbers: Give an array numbers of n integer * @return: Find all unique triplets in the array which gives the sum of zero. */ //三数之和 //给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。 public List<List<Integer>> threeSum(int[] numbers) { // write your code here List<List<Integer>> list=new ArrayList<>(); if(numbers==null || numbers.length<3) return list; Arrays.sort(numbers); for(int i=0;i<numbers.length;i++){ //跳过与当前i元素相同的元素 if(i>0 && numbers[i]==numbers[i-1]) continue; int l=i+1;int r=numbers.length-1; //类似于双指针 int target=-numbers[i]; twosum(numbers,l,r,target,list); } return list; } //求两数之和 public void twosum(int[] numbers,int left,int right,int target,List<List<Integer>> lists){ while(left<right){ int sum=numbers[left]+numbers[right]; if(sum==target){ List<Integer> list=new ArrayList<>(); list.add(-target); list.add(numbers[left]); list.add(numbers[right]); lists.add(list); left++; right--; //跳过与当前left元素相同的元素 while(left<right && numbers[left]==numbers[left-1]){ left++; } while(left<right && numbers[right]==numbers[right+1]){ right--; } }else if(sum<target){ left++; }else{ right--; } } }
题目:
分析:和三数之和思想类似,只不过这次先固定两个指针,两个for循环确定首尾两个指针后,通过调整剩下两个指针来与target比较。
public class Solution { /** * @param numbers: Give an array * @param target: An integer * @return: Find all unique quadruplets in the array which gives the sum of zero */ public List<List<Integer>> fourSum(int[] numbers, int target) { // write your code here List<List<Integer>> list=new ArrayList<>(); if(numbers==null || numbers.length<3) return list; Arrays.sort(numbers); for(int i=0;i<numbers.length;i++){ //跳过与当前i元素相同的元素 if(i>0 && numbers[i]==numbers[i-1]) continue; for(int j=numbers.length-1;j>i;j--) { if(j<numbers.length-1 && numbers[j]==numbers[j+1]) continue; int l = i + 1; int r = j - 1; //类似于双指针 int subtarget = target-numbers[i]-numbers[j]; twosum(numbers, l, r,i,j,subtarget, list); } } return list; } //求两数之和 public void twosum(int[] numbers,int left,int right,int fixI,int fixJ,int target,List<List<Integer>> lists){ while(left<right){ int sum=numbers[left]+numbers[right]; if(sum==target){ List<Integer> list=new ArrayList<>(); list.add(numbers[fixI]); list.add(numbers[left]); list.add(numbers[right]); list.add(numbers[fixJ]); lists.add(list); left++; right--; //跳过与当前left元素相同的元素 while(left<right && numbers[left]==numbers[left-1]){ left++; } while(left<right && numbers[right]==numbers[right+1]){ right--; } }else if(sum<target){ left++; }else{ right--; } } } }
题目:
分析:和三数之和思想类似,将数组排序后,通过固定一个元素,调整另外两个来得到三数之和。用一个变量存储当前找到的最接近target的三数之和。
public class Solution { /** * @param numbers: Give an array numbers of n integer * @param target: An integer * @return: return the sum of the three integers, the sum closest target. */ public int threeSumClosest(int[] numbers, int target) { // write your code here if(numbers==null || numbers.length<3) return -1; Arrays.sort(numbers); int min=Integer.MAX_VALUE; for(int i=0;i<numbers.length;i++){ //跳过与当前元素相同的元素 if(i>0 && numbers[i]==numbers[i-1]) continue; int l=i+1; int r=numbers.length-1; //类似于双指针 while(l<r){ int sum=numbers[l]+numbers[r]+numbers[i]; if(Math.abs(sum-target)<Math.abs(min-target)) min=sum; if(sum<target) l++; else r--; } } return min; } }