三数之和

题目描述

链接
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

思路

  1. 回溯法(提交超时)
    1.1 0-1背包思路
  • 类似 0-1 背包问题,在 n 个整数中选择 3 个数,3 个数的和为 0。
  • 一个元素占树的一层,按深度优先遍历树,边为 0 表示不选这个数,边为1 表示选这个数。
  • 回溯搜索过程,当过程集中当前整数个数等于 3 且和当前和等于 0,则把过程集加入结果集中。
  • 对于去重处理,用到排序+LinkedHashSet,其中LinkedHashSet是有序的
    假设输入:nums = [-1,0,1,2],过程如图:


    image.png

Java 代码如下:

class Solution {
    LinkedHashSet<List<Integer>> result = new LinkedHashSet<>();
    List<Integer> way = new ArrayList<>();
    public List<List<Integer>> threeSum(int[] nums) {
        if (nums.length < 3) {
            return new ArrayList<>();
        }
        Arrays.sort(nums);
        int num = 3;
        int sum = 0;
        //当前数量
        int currentNum = 0;
        //当前和
        int currentSum = 0;
        backTrack(nums, num, sum, 0, nums.length, currentNum, currentSum);
        return new ArrayList<>(result);
    }
      public void backTrack(int[] nums, int num, int sum, int t, int len, int currentNum, int currentSum) {
        if (t > len - 1 || currentNum >= num) {
            if (currentNum == num && currentSum == sum) {
                List temp = new ArrayList(way);
                result.add(temp);
            }
            return ;
        }
        if (currentSum > 0 && nums[t] >= 0) {
            return;
        }
        if ((currentNum + 1) <= num) {
            ++currentNum;
            currentSum += nums[t];
            way.add(nums[t]);
            backTrack(nums, num, sum, t + 1, len, currentNum, currentSum);
            --currentNum;
            currentSum -= nums[t];
            way.remove(way.size() - 1);
        }
        backTrack(nums, num, sum, t + 1, len, currentNum, currentSum);
    }
}

1.2 选择回溯思路

  • 回溯法还一种写法是从剩下的元素中选择一个加到过程集,相比1.1 中,遍历层数更少,性能更优。
    假设输入:nums = [-1,0,1,2,-1,4],过程如图:


    image.png

Java 代码如下:

 public static void backTrack(int[] nums, int t, int target) {
        if (way.size() == 3) {
            if (way.get(0) + way.get(1) + way.get(2)  == target) {
                if (!result.contains(way)) {
                    List temp = new ArrayList(way);
                    Collections.sort(temp);
                    result.add(temp);
                }
            }
            return;
        }
        for (int i = t; i < nums.length; i++) {
            if(nums.length-i < 3-way.size()){
                return;
            }
            way.add(nums[i]);
            backTrack(nums, i + 1, target);
            way.remove(way.size() - 1);
        }
    }
  1. 三指针法
  • 首先对数组排序
  • 选择一个数 nums[j],再用左右指针指向 nums[j]后面的两端。
  • 计算三个数和。当和等于 0时,则将三个数加入结果集,此时,如果左指针当前值与下一个值相等,则跳过,即 l++,如果右指针当前值与下一个值相等,则跳过,即 r++;当和大于 0 时,需要往左边小的数字寻找答案,右指针往左走;当和小于 0 时,需要往右边大的数字寻找答案,左指针往右走。
  • j也要跳过重复元素。
    Java 代码如下:
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        if(nums.length == 0){
            return result;
        }
        Arrays.sort(nums);
        for(int j = 0;j < nums.length;j++){
            int l = j+1;
            int r = nums.length-1;
            // System.out.println("j"+nums[j]);
            while(l < r){
                // System.out.println("j"+nums[j]);
                // System.out.println("l"+nums[l]);
                // System.out.println("r"+nums[r]);
                int sum = nums[j]+nums[l]+nums[r];
                // System.out.println("sum"+sum);
                if(sum == 0){
                    List<Integer> way = new ArrayList<Integer>();
                    way.add(nums[j]);
                    way.add(nums[l]);
                    way.add(nums[r]);
                    result.add(way);
                    while((l+1) < nums.length && nums[l] == nums[l+1]){
                        l++;
                    }
                    l++;
                    while((r-1) >=0 && nums[r] == nums[r-1]){
                        r--;
                    }
                    r--;
                }else if(sum > 0){
                     while((r-1) >=0 && nums[r] == nums[r-1]){
                        r--;
                    }
                    r--;
                }else{
                     while((l+1) < nums.length && nums[l] == nums[l+1]){
                        l++;
                    }
                    l++;
                }
            }
            while((j+1) < nums.length && nums[j] == nums[j+1]){
                        j++;
            }
        }
        return result;
    }
}

C++代码如下:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        if(nums.size() == 0){
            return result;
        }
       sort(nums.begin(),nums.end());
        for(int j = 0;j<nums.size();j++){
            int l = j+1;
            int r = nums.size()-1;
            while(l<r){
                int sum = nums[j]+nums[l]+nums[r];
                if(sum == 0){
                    vector<int> way;
                    way.push_back(nums[j]);
                    way.push_back(nums[l]);
                    way.push_back(nums[r]);
                    result.push_back(way);
                    while((l+1) < nums.size() && nums[l] == nums[l+1]){
                        l++;
                    }
                    l++;
                    while((r-1) >= 0 && nums[r] == nums[r-1]){
                        r--;
                    }
                    r--;
                }else if(sum > 0){
                    while((r-1) >= 0 && nums[r] == nums[r-1]){
                        r--;
                    }
                    r--;
                }else{
                    while((l+1) < nums.size() && nums[l] == nums[l+1]){
                        l++;
                    }
                    l++;
                }
                while((j+1) < nums.size() && nums[j] == nums[j+1]){
                    j++;
                }
            }
        }
        return result;
    }
};

遇到的问题

  1. 去重处理
  • 回溯法中的去重主要用到排序+LinkedHashSet,LinkedHashSet是Set集合的一个实现,继承自HashSet,内部使用的是LinkHashMap。具有set集合不重复的特点,同时是有序的,是一个非线程安全的集合。HashSet继承自AbstractSet,实现了Set接口,内部使用HashMap来存储数据,数据存储在HashMap的key中,value都是同一个默认值。
  • 三指针法中的去重主要是排序+判断值相等则继续移动
  1. Java 中数组和链表排序的方法区分
  • Arrays中的sort()方法主要是针对各种数据类型(基本数据类型和引用对象类型)的数组元素排序。
  • java.util.Collections中的静态方法的Collection.sort()主要是针对集合框架中的动态数组,链表,树,哈希表等( ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap )进行排序。

扩展

四数之和

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容