采用三种方法,两种暴力,一种前后指针,相比来说,前后指针易懂,且时间复杂度为O(n^2),空间复杂度为O(1),比较友好,其他两种方法,写到一半,感觉太麻烦,没写。
public class ThreeSum {
//暴力1:去重麻烦,且时间复杂度为O(N^3)
public List<List<Integer>> threeSum1(int[] nums) {
int n=nums.length;
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < n-2; i++) {//i的最后一位为n-3
for (int j = i+1; j < n-1; j++) {
for (int k = j+1; k < n; k++) {
if (nums[i]+nums[j]+nums[k]==0)
result.add(Arrays.asList(nums[i],nums[j],nums[k]));//数组转list加入list
}
}
}
return result;
}
//暴力2:哈希表,逻辑太复杂,去重更麻烦
public List<List<Integer>> threeSum2(int[] nums) {
int n=nums.length;
List<List<Integer>> result = new ArrayList<>();
//hashmap中key为除了要找的那个数之外的两个数之和,value为另外两个数
HashMap<Integer, List<Integer>> hashMap = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n-1; j++) {
hashMap.put(nums[i]+nums[j],Arrays.asList(nums[i],nums[j]));
}
}
for (int i = 0; i < n; i++) {
//nums[i]为那个数。thatNum为key
int thatNum=0-nums[i];
if (hashMap.containsKey(thatNum)){
//已经在表中找到另外两数
List<Integer> list = hashMap.get(thatNum);
Integer[] array = (Integer[]) list.toArray();
result.add(Arrays.asList(nums[i],array[0],array[1]));
}
}
return result;
}
//前后指针:时间复杂度O(N^2)
public List<List<Integer>> threeSum3(int[] nums){
int n = nums.length;
List<List<Integer>> result = new ArrayList<>();
// 先对数组进行排序,快排O(NlogN)
Arrays.sort(nums);
for( int i = 0; i < n; i++ ){
//核心大于0,则左右指针必大于0,退出
if( nums[i] > 0 )
break;
//相等的核心跳过,去重,并且防止数组越界,保证核心为最左边的数,也防止了下面左指针会和核心相等的情况
if( i > 0 && nums[i] == nums[i-1] )
continue;
// 定义左右指针(索引位置)
int lp = i + 1;
int rp = n - 1;
// 只要左右不重叠,就继续移动指针
while( lp < rp ){
int sum = nums[i] + nums[lp] + nums[rp];
if( sum == 0 ){
result.add(Arrays.asList(nums[i], nums[lp], nums[rp]));
lp ++;
rp --;
//lp<rp防止rp=lp,如果左右指针相同,向内移动
while( lp < rp && nums[lp] == nums[lp - 1] )
lp ++;
while( lp < rp && nums[rp] == nums[rp + 1] )
rp --;
}
//左指针右移,增大值
else if( sum < 0 )
lp ++;
else//右指针左移,减小值
rp --;
}
}
return result;
}
public static void main(String[] args) {
int[] nums = {-1,0,1,2,-1,-4,-2,-3,3,0,4};
ThreeSum threeSum = new ThreeSum();
List<List<Integer>> lists = threeSum.threeSum3(nums);
System.out.println(lists);
}
}
涉及知识点:
1.list已经重写tostring,可以直接打印,并且有序可重复,set无序不可重复。
2.数组可以直接转为list,list也可转数组。
3.前后指针实际上有分治的思想
4.左右指针相遇,结束循环
5.一般说的时间复杂度都为平均复杂度
6.元组一般无顺序一说。
7.暴力法搜索时间复杂度为O(N^3),要进行优化,可通过双指针动态消去无效解来提高效率。