From 0eb1de8bcf9a485bbfb6162a37d1fc28fa1ab10f Mon Sep 17 00:00:00 2001 From: dingjiawen <745518019@qq.com> Date: Thu, 8 Sep 2022 15:47:22 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E5=93=88=E5=B8=8C=E8=A1=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../markilue/leecode/hashtable/FourSum.java | 180 +++++++++++++++++ .../leecode/hashtable/FourSumCount.java | 186 ++++++++++++++++++ .../markilue/leecode/hashtable/ThreeSum.java | 81 ++++++++ 3 files changed, 447 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/hashtable/FourSum.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hashtable/FourSumCount.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hashtable/ThreeSum.java diff --git a/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSum.java b/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSum.java new file mode 100644 index 0000000..8682409 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSum.java @@ -0,0 +1,180 @@ +package com.markilue.leecode.hashtable; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.hashtable + * @Author: dingjiawen + * @CreateTime: 2022-09-08 12:24 + * @Description: TODO 力扣18题:四数之和 + * 给你一个由 n 个整数组成的数组nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复): + * 1)0 <= a, b, c, d< n + * 2)a、b、c 和 d 互不相同 + * 3)nums[a] + nums[b] + nums[c] + nums[d] == target + * 你可以按 任意顺序 返回答案 。 + * @Version: 1.0 + */ +public class FourSum { + + @Test + public void test() { + int[] nums = {2, 2, 2, 2, 2}; + int target = 8; + System.out.println(fourSum1(nums, target)); + } + + @Test + public void test1() { + int[] nums = {1,0,-1,0,-2,2}; + int target = 0; + System.out.println(fourSum1(nums, target)); + } + + @Test + public void test2() { + int[] nums = {-1,0,1,2,-1,-4}; + int target = -1; + System.out.println(fourSum1(nums, target)); + } + + @Test + public void test3() { + int[] nums = {1,-2,-5,-4,-3,3,3,5}; + int target = -11; + System.out.println(fourSum1(nums, target)); + } + + @Test + public void test4() { + int[] nums = {1000000000,1000000000,1000000000,1000000000}; + int target = -294967296; + System.out.println(1000000000+1000000000); + System.out.println(-294967296-(1000000000+1000000000)); + System.out.println(fourSum1(nums, target)); + } + + + + /** + * 本人思路:这里如果还想使用双指针法,时间复杂度会是O(n^3),考虑还是使用hash法 + * 维护一个hash表<两数之和,list<两数>> 如何避免两数重复? 先排序在加入,但是有出现了问题:虽然不重复,但是会存在覆盖问题 + * 如下面的实现所示 + * + * @param nums + * @param target + * @return + */ + public List> fourSum(int[] nums, int target) { + if (nums.length < 4) { + return new ArrayList<>(); + } + Arrays.sort(nums); + HashMap> map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + for (int j = i + 1; j < nums.length; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) { + continue; + } + map.put(nums[i] + nums[j], new ArrayList<>(Arrays.asList(nums[i], nums[j]))); + } + } + + + List> lists = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + for (int j = i + 1; j < nums.length; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) { + continue; + } + if (map.containsKey(target - nums[i] - nums[j])) { + map.get(target - nums[i] - nums[j]).add(nums[i]); + map.get(target - nums[i] - nums[j]).add(nums[j]); + lists.add(map.get(target - nums[i] - nums[j])); + map.get(target - nums[i] - nums[j]).remove(3); + map.get(target - nums[i] - nums[j]).remove(4); + } + map.put(nums[i] + nums[j], new ArrayList<>(Arrays.asList(nums[i], nums[j]))); + } + } + + + Collection> values = map.values(); + for (List value : values) { + lists.add(value); + } + + + return lists; + + } + + /** + * 代码思想录思路:还是使用双指针法,在外面在套一层循环,将四数问题转化为3数问题 + * 如下所示 + * 速度超过49.9%,内存超过93.76% + * @param nums + * @param target + * @return + */ + public List> fourSum1(int[] nums, int target) { + if (nums.length < 4) { + return new ArrayList<>(); + } + Arrays.sort(nums); + + List> lists = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + for (int j = i + 1; j < nums.length; j++) { + + if (j > i + 1 && nums[j] == nums[j - 1]) { + continue; + } + int mid = j + 1; + int right = nums.length - 1; + + while (mid < right) { + //解决int超过限制的问题 + long sum = (long) nums[i] + nums[j] + nums[mid] + nums[right]; + if (sum < target) { + mid++; + } else if (sum > target) { + right--; + } else { + lists.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[mid], nums[right]))); + while (mid < right && nums[mid] == nums[mid + 1]) { + mid++; + } + while (mid < right && nums[right] == nums[right - 1]) { + right--; + } + mid++; + right--; + } + } + + } + } + + + return lists; + + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSumCount.java b/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSumCount.java new file mode 100644 index 0000000..398c834 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hashtable/FourSumCount.java @@ -0,0 +1,186 @@ +package com.markilue.leecode.hashtable; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.hashtable + * @Author: dingjiawen + * @CreateTime: 2022-09-08 09:09 + * @Description: + * TODO leecode454题:四数相加II: + * 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: + * 1) 0 <= i, j, k, l < n + * 2) nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0 + * @Version: 1.0 + */ +public class FourSumCount { + + + @Test + public void test(){ + + int[] nums1 = {1, 2}; + int[] nums2 = {-2, -1}; + int[] nums3 = {-1, 2}; + int[] nums4 = {0, 2}; + System.out.println(fourSumCount1(nums1, nums2, nums3, nums4)); + + } + + @Test + public void test2(){ + + int[] nums1 = {1, 2}; + int[] nums2 = {-2, -1}; + int[] nums3 = {0, 2}; + int[] nums4 = {0, 2}; + System.out.println(fourSumCount1(nums1, nums2, nums3, nums4)); + + } + + @Test + public void test1(){ + + int[] nums1 = {0}; + int[] nums2 = {0}; + int[] nums3 = {0}; + int[] nums4 = {0}; + System.out.println(fourSumCount1(nums1, nums2, nums3, nums4)); + + } + + + /** + * 自己的思路: + * 1)使用递归来解决,把四数相加转换为三数相加转为两数相加,这种方式时间复杂度直接O(n^4) + * 2)构建两个hash表,分别遍历两个数组,时间复杂度O(n^2) + * 以下是第二种思路,hashset暂时还有问题没有解决,如test2的问题 + * @param nums1 + * @param nums2 + * @param nums3 + * @param nums4 + * @return + */ + public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { + + + //构建一个hash表, + HashSet set1 = new HashSet<>(); + + for (int i = 0; i < nums1.length; i++) { + for (int j = 0; j < nums2.length; j++) { + set1.add(nums1[i]+nums2[j]); + } + } + + + HashSet set2 = new HashSet<>(); + + for (int i = 0; i < nums3.length; i++) { + for (int j = 0; j < nums4.length; j++) { + set2.add(nums3[i]+nums4[j]); + } + } + + int count=0; + for (Integer integer : set1) { + if(set2.contains(0-integer)){ + count++; + } + } + + + return count; + + } + + /** + * 代码思想录的思路: + * 2)构建两个hash表<两数之和,这两数之和出现的次数>,分别遍历两个数组,时间复杂度O(n^2) + * 速度击败20.33%,内存击败13.78% + * @param nums1 + * @param nums2 + * @param nums3 + * @param nums4 + * @return + */ + public int fourSumCount1(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { + + //构建一个hash表<两数之和,这两数之和出现的次数> + HashMap map1 = new HashMap<>(); + + for (int i = 0; i < nums1.length; i++) { + for (int j = 0; j < nums2.length; j++) { + map1.put(nums1[i]+nums2[j],map1.getOrDefault(nums1[i]+nums2[j],0)+1); + } + } + + + HashMap map2 = new HashMap<>(); + + for (int i = 0; i < nums3.length; i++) { + for (int j = 0; j < nums4.length; j++) { + map2.put(nums3[i]+nums4[j],map2.getOrDefault(nums3[i]+nums4[j],0)+1); + } + } + + int count=0; + for (Integer integer : map1.keySet()) { + if(map2.containsKey(0-integer)){ + count+=map1.get(integer)*map2.get(0-integer); + } + } + + + + return count; + + } + + /** + * 官方的思路: + * 2)构建一个hash表<两数之和,这两数之和出现的次数>,分别遍历两个数组,时间复杂度O(n^2) + * 速度击败28.59%,内存击败91.98% 时间空间复杂度均为O(n^2) + * @param nums1 + * @param nums2 + * @param nums3 + * @param nums4 + * @return + */ + public int fourSumCount2(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { + + //构建一个hash表<两数之和,这两数之和出现的次数> + HashMap map1 = new HashMap<>(); + + for (int i = 0; i < nums1.length; i++) { + for (int j = 0; j < nums2.length; j++) { + map1.put(nums1[i]+nums2[j],map1.getOrDefault(nums1[i]+nums2[j],0)+1); + } + } + + int count=0; + + for (int i = 0; i < nums3.length; i++) { + for (int j = 0; j < nums4.length; j++) { + if(map1.containsKey(0-nums3[i]-nums4[j])){ + count+=map1.get(0-nums3[i]-nums4[j]); + } + } + } + + return count; + + } + + + + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hashtable/ThreeSum.java b/Leecode/src/main/java/com/markilue/leecode/hashtable/ThreeSum.java new file mode 100644 index 0000000..5dcb089 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hashtable/ThreeSum.java @@ -0,0 +1,81 @@ +package com.markilue.leecode.hashtable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.hashtable + * @Author: dingjiawen + * @CreateTime: 2022-09-08 10:49 + * @Description: + * TODO 力扣15题:三数之和: + * 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请 + * 你返回所有和为 0 且不重复的三元组。 + * 注意:答案中不可以包含重复的三元组。 + * @Version: 1.0 + */ +public class ThreeSum { + + /** + * 这道题使用哈希法的思路: + * 使用两层循环,先计算出a+b,最后再遍历一遍找到-a-b的值,最后再进行去重,整体时间复杂度是O(n^2),但是仍然是比较费时的操作,因为去重比较麻烦 + * 这里可以使用双指针法:先对数组进行整体的排序,作为一个有序的数组,使用头尾指针,寻找中间的数据,时间复杂度O(n^2) + * @param nums + * @return + */ + public List> threeSum(int[] nums) { + + if(nums.length<3){ + return new ArrayList<>(); + } + + Arrays.sort(nums); + + List> lists = new ArrayList<>(); + + + for (int left = 0; left < nums.length - 2; left++) { + + if(nums[left]>0){ + break; + } + //避免重复 + if(left>0&&nums[left]==nums[left-1]){ + continue; + } + + //定义尾指针 + int right= nums.length-1; + //定义中间指针 + int mid =left+1; + while (mid0){ + right--; + }else if(sum<0){ + mid++; + }else { + //sum==0 + ArrayList list = new ArrayList<>(Arrays.asList(nums[left],nums[mid],nums[right])); + //判断一下左右避免重复 + while (mid