zoukankan      html  css  js  c++  java
  • 题解LeetCode--三数之和

    三数之和--LeetCode015


    我的LeetCode代码集:https://github.com/cnamep001/LeetCode

    原题链接:https://leetcode-cn.com/problems/3sum/description/




    题目描述:

    img

    知识点:哈希表,对撞双指针



    思路一:第一个指针从前往后遍历,另外两个指针在第一个指针设定的范围内对撞查找

    (1)首先对数组nums进行从小到大排序,此步骤可以调用Java中的库函数Arrays.sort()。

    (2)设置一个索引指针i依次从左到右遍历排序之后的nums数组。

    (3)对于每一个nums[i],我们假设nums[i]是所要寻找的三个数中的最小数。那么如果nums[i] > 0,显然在本次循环中不可能找到另外两个数num[left]和nums[right],使得满足条件nums[left] + nums[right] == 0 - nums[i],我们直接break结束整个循环。

    (4)如果i > 1且nums[i] == nums[i - 1],那么所找到的三个数的组合一定是一样的,我们直接continue跳过本轮i循环的后面的所有语句,直接i++进入下一轮循环。

    (5)设置left的初值为i + 1,right的初值为nums.length() - 1。当left < right时,我们可以一直寻找nums[left]和nums[right]这两个值。因此我们可以再在for循环内部设置一个while循环,条件是while(left < right)。

    (6)如果nums[left] + nums[right] == 0 - nums[i],那么我们找到了一组三个数,我们将这三个数保存起来,并且令left++,right--。此时我们需要注意题目的要求:答案中不可以包含重复的三元组,因此如果left++以后的nums[left]和之前的nums[left]相等且满足条件left < right的话,我们依旧需要设置一个循环另left++,直到找到一个不同的元素为止。同理对right--进行同样的操作,如果right--以后的nums[right]和之前的nums[right]相等且满足条件left < right的话,我们依旧需要right--。

    (7)如果nums[left] + nums[right] > 0 - nums[i],那么说明所寻找的两数太大了,我们需要进行right--的操作。同理为了避免重复三元组的出现,我们需要进行(6)中对right指针的操作。

    (8)如果nums[left] + nums[right] < 0 - nums[i],那么说明所寻找的两数太小了,我们需要进行left++的操作。同理为了避免重复三元组的出现,我们需要进行(6)中对left指针的操作。

    首先,我们有一个排序过程,是O(nlog(n))级别的复杂度,其中n为nums数组的长度。而对于对撞双指针,我们的时间复杂度可以看成是O(n)级别的,而其外面还有一个指针在遍历整个数组,因此总的时间复杂度是O(n ^ 2)级别的。空间复杂度是O(1)级别的,整个过程中我们没有使用额外的空间。

    JAVA代码:

    package com.m.lc_threenum.solution3;
    
    
    import java.util.*;
    
    public class Solution {
    
        public List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> listList = new ArrayList<>();
            Arrays.sort(nums);
            for (int i = 0; i < nums.length - 2; i++) {
                if (nums[i] > 0) {
                    break;
                }
                if (i > 0 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.length - 1;
                while (left < right) {
                    if (nums[i] + nums[left] + nums[right] == 0) {
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[left]);
                        list.add(nums[right]);
                        listList.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[i] + nums[left] + nums[right] < 0) {
                        left++;
                        while (left < right && nums[left] == nums[left - 1]) {
                            left++;
                        }
                    } else {
                        right--;
                        while (left < right && nums[right] == nums[right + 1]) {
                            right--;
                        }
                    }
                }
            }
            return listList;
        }
    
    }
    
    

    LeetCode解题报告:


    测试代码:

    package com.m.lc_threenum;
    
    
    public class Test1 {
        public static void main(String[] args) {
    
            int [] arr = new int[]{-1, 0, 1, 2, -1, 4};
            Solution solution = new Solution1();
            System.out.println(solution.threeSum(arr));    //  [[-1, -1, 2], [-1, 0, 1]]
    
        }
    }
    
    




    思路二:用一个哈希表来存储数组中的所有元素及其出现的次数

    我们用一个哈希表来存储数组中出现的所有元素及其出现的次数。

    我们总共分三种情况:

    (1)三个数字相同:如果0这个数字在哈希表中记录的次数等于或者大于3次,那么3个0是一个组合,将其记录在结果中。

    (2)两个数字相同:在我们用哈希表记录了数组中所有元素及其出现的次数以后,我们就可以对数组进行过滤,将重复的元素剔除掉得到一个新的数组集合ArrayList,里面没有重复元素。对这个集合进行一次排序操作。再用双重循环遍历集合。

    a.当arrayList.get(i) * 2 + arrayList.get(j) == 0且在哈希表中arrayList.get(i)出现的次数大于等于2次。

    b.当arrayList.get(i) + arrayList.get(j) * 2 == 0且在哈希表中arrayList.get(j)出现的次数大于等于2次。

    (3)三个数字都不同:在哈希表中- arrayList.get(i) - arrayList.get(j)出现的次数大于等于1次且- arrayList.get(i) - arrayList.get(j) > arrayList.get(j)。

    将上述三种情况的结果都保存在结果中,最终返回总结果即可。

    这个思路过程中生成哈希表这一过程的时间复杂度是O(n),其中n为数组的长度。排序arrayList集合的时间复杂度是O(mlogm),其中m为数组中不重复元素的个数。遍历arrayList的时间复杂度是O(m ^ 2)。而空间复杂度由于使用了哈希表,一定是O(n)级别的。

    JAVA代码:

    package com.m.lc_threenum.solution2;
    
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.List;
    
    public class Solution2 {
    
    
        public List<List<Integer>> threeSum(int[] nums) {
    
    
            List<List<Integer>> listList = new ArrayList<>();
    
    
            HashMap<Integer, Integer> hashMap = new HashMap<>();
    
    
            for (int i = 0; i < nums.length; i++) {
                if (hashMap.containsKey(nums[i])) {
                    hashMap.put(nums[i], hashMap.get(nums[i]) + 1);
    
                } else {
                    hashMap.put(nums[i], 1);
    
                }
    
            }
    
    
            if (hashMap.containsKey(0) && hashMap.get(0) >= 3) {
                addToListList(0, 0, 0, listList);
            }
    
            ArrayList<Integer> arrayList = new ArrayList<>();
    
            for (Integer integer : hashMap.keySet()) {
    
                arrayList.add(integer);
    
            }
    
    
            Collections.sort(arrayList);
    
    
            for (int i = 0; i < arrayList.size(); i++) {
    
    
                for (int j = i + 1; j < arrayList.size(); j++) {
    
    
                    if (arrayList.get(i) * 2 + arrayList.get(j) == 0 && hashMap.get(arrayList.get(i)) >= 2) {
    
    
                        addToListList(arrayList.get(i), arrayList.get(i), arrayList.get(j), listList);
    
                    }
    
                    if (arrayList.get(i) + arrayList.get(j) * 2 == 0 && hashMap.get(arrayList.get(j)) >= 2) {
    
    
                        addToListList(arrayList.get(i), arrayList.get(j), arrayList.get(j), listList);
    
    
                    }
    
    
                    int num = -arrayList.get(i) - arrayList.get(j);
    
    
                    if (num > arrayList.get(j) && hashMap.containsKey(num)) {
    
    
                        addToListList(arrayList.get(i), arrayList.get(j), num, listList);
    
    
                    }
    
    
                }
    
    
            }
    
    
            return listList;
    
    
        }
    
    
        private void addToListList(int num1, int num2, int num3, List<List<Integer>> listList) {
    
            List<Integer> list = new ArrayList<>();
    
            list.add(num1);
    
            list.add(num2);
    
            list.add(num3);
    
            listList.add(list);
    
    
        }
    
    
    }
    
    

    LeetCode解题报告:


    测试代码:

    package com.m.lc_threenum.solution2;
    
    public class Test2 {
        public static void main(String[] args) {
            int [] arr = new int[]{-1, 0, 1, 2, -1, 4};
            Solution2 solution2 = new Solution2();
            System.out.println(solution2.threeSum(arr));    //  [[-1, -1, 2], [-1, 0, 1]]
        }
    }
    
    
  • 相关阅读:
    一张图片入门Python
    4.1. 如何在Windows环境下开发Python
    你必须知道的EF知识和经验
    XUnit的使用
    如何使用NUnit
    Entity Framework 不支持DefaultValue
    Have You Ever Wondered About the Difference Between NOT NULL and DEFAULT?
    Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
    Entity Framework 与多线程
    sqlite中的自增主键
  • 原文地址:https://www.cnblogs.com/k-class/p/13781145.html
Copyright © 2011-2022 走看看