力扣15 三数之和

题目:

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

哈希解法:

class Solution {
    public static List<List<Integer>> threeSum(int[] nums) {
        HashSet<List<Integer>> set = new HashSet<>();
        for (int i=0;i<nums.length-2;i++){
            HashMap<Integer, Integer> map = new HashMap<>();
            for (int j=i+1;j<nums.length;j++){//i和j不重复
                int need=-(nums[i]+nums[j]);
                if (map.containsKey(need)){
                    List<Integer> list = Arrays.asList(nums[i], nums[j], need);
                    Collections.sort(list);
                    set.add(list);////排序后存放在set中的集合能保证没有重复
                }else {
                    map.put(nums[j], nums[j]);//存的第三个数和本轮的j永远不会相同
                }
            }
        }
        return new ArrayList<>(set);//set转list返回
    }
}

双指针法:

一层循环,i指第一个数,left指针指j,right指针指k

先对给定数组进行排序,保证左小右大。然后i控制循环次数,也代表了三元组的第一个数。

在left和right相遇之前:

判断nums[left]+nums[right]+nums[i]>0,right往左走

判断nums[left]+nums[right]+nums[i]<0,left往右走

直至left和right相遇(因为left和right交换之后其实又是之前的组合)

期间如果遇到nums[left]+nums[right]+nums[i]=0,则将该[i,left,right]放入list

关于去重:

当确定了一个三元组后left或者right怎么移动,首先需要跳至nums[left]/nums[right]数值相同的下标位置,然后left和right都向前走一步。

-5 -3 -1 0 0 1 2 3 3 3 8
  i   left--> left left right right   <--right  

 

class Solution {
    public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
         Arrays.sort(nums);
        //找出a + b + c = 0
        //a = nums[i], b = nums[left], c = nums[right]
         for (int i = 0; i < nums.length; i++) {
        //排序之后若第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果
            if (nums[i] > 0) { 
                return result;
            }

            if (i > 0 && nums[i] == nums[i-1]) {  //去重a //a移动到相等数值的那个下标位置
                continue;
            }

            int left = i + 1;
            int right = nums.length - 1;
            while (right > left) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    //去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    //right和left移动到相等数值的那个下标位置
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    
                    //往前走一步,开始下一轮
                    right--; 
                    left++;
                }
            }
        }
        return result;
    }
}

 

posted @ 2022-11-30 21:32  壹索007  阅读(19)  评论(0编辑  收藏  举报