美文网首页
三数之和

三数之和

作者: twilight_mao | 来源:发表于2021-02-20 11:16 被阅读0次

    题目描述

    链接
    给你一个包含 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 )进行排序。

    扩展

    四数之和

    相关文章

      网友评论

          本文标题:三数之和

          本文链接:https://www.haomeiwen.com/subject/ynyztltx.html