LeetCode | 3 Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
- Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
- The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4}, A solution set is: (-1, 0, 1) (-1, -1, 2)
方法一:
//基本的brute force方法:在eclipse上运行是可以的,也满足三元组中的非降序 //及无重复三元组的要求。但是提示超时,不accept。算法复杂度为O(n^3) public class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); if(nums.length < 3) return result; for(int i=0; i<nums.length; i++){ for(int j=i+1; j<nums.length; j++){ for(int k=j+1; k<nums.length; k++){ if(nums[i]+nums[j]+nums[k] == 0){ Integer[] array = {nums[i], nums[j], nums[k]}; Arrays.sort(array); //对三元组排序 List<Integer> list = new ArrayList<Integer>(); list.addAll(Arrays.asList(array)); //addAll方法参数只能为collection if(!result.contains(list)){ //能够达到去除重复三元组的效果 result.add(list); } } } } } return result; } }
方法二:
/引入二分查找,要求返回数组元素值而非index,故可先排序,再找元素 //理论上,算法复杂度为O(n^2*logn),在eclipse上执行也可以(没细对比,可能有误) //提示超时,不accept public class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); if(nums.length < 3) return result; Arrays.sort(nums); for(int i=0; i<nums.length; i++){ for(int j=i+1; j<nums.length; j++){ int head = j + 1; int tail = nums.length - 1; int needNum = -(nums[i]+nums[j]); while(head <= tail){ //二分查找 int middle = (head+tail) / 2; if(nums[middle] == needNum){ List<Integer> list = new ArrayList<Integer>(); list.add(nums[i]); list.add(nums[j]); //已经排序过了,无需再排序了 list.add(nums[middle]); if(!result.contains(list)){ result.add(list); } break; //二分查找到结果之后,一定要break,否则head=tail时会无限循环 }else if(nums[middle] > needNum){ tail = middle - 1; }else if(nums[middle] < needNum){ head = middle + 1; } } } } return result; } }
方法三:(accept)
//排序花费O(nlogn),遍历花费O(n^2),总复杂度为O(n^2) public class Solution { //主调方法 public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); if(nums.length < 3) return result; Arrays.sort(nums); for(int i=nums.length-1; i>=2; i--){ //每次取nums[i],然后在nums[0~i-1]中寻找两和为-nums[i]的 if(i<nums.length-1 && nums[i]==nums[i+1]){ //遇到重复的nums[i]就跳过 continue; } //方法调用,找sum为-nums[i]的pair List<List<Integer>> tempCollection = twoSum(nums, i-1, -nums[i]); for(int j=0; j<tempCollection.size(); j++){ List<Integer> tempList = tempCollection.get(j); tempList.add(nums[i]); //把二元组扩充为三元组 result.add(tempList); } } return result; } //被threeSum调用的辅助方法,思想类似于昨天twoSum中的夹逼思想,但是要注意跳过重复的元素 //下面的方法中,参数nums是应经排序过的数组,故调用此方法只需要O(n)的复杂度 private List<List<Integer>> twoSum(int[] nums, int end, int target) { List<List<Integer>> result = new ArrayList<List<Integer>>(); if(nums.length < 2) return null; int left = 0; int right = end; while(left < right){ //夹逼的思想 if(nums[left]+nums[right] == target){ List<Integer> list = new ArrayList<Integer>(); list.add(nums[left]); list.add(nums[right]); result.add(list); left++; right--; while(left<right && nums[left]==nums[left-1]){ //跳过重复的元素 left++; } while(left<right && nums[right]==nums[right+1]){ //跳过重复的元素 right--; } }else if(nums[left]+nums[right] > target){ right--; }else if(nums[left]+nums[right] < target){ left++; } } return result; } }