leecode哈希表更新

This commit is contained in:
dingjiawen 2022-09-08 15:47:22 +08:00
parent 84ad855669
commit 0eb1de8bcf
3 changed files with 447 additions and 0 deletions

View File

@ -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]]若两个四元组元素一一对应则认为两个四元组重复
* 10 <= a, b, c, d< n
* 2abc d 互不相同
* 3nums[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<List<Integer>> fourSum(int[] nums, int target) {
if (nums.length < 4) {
return new ArrayList<>();
}
Arrays.sort(nums);
HashMap<Integer, List<Integer>> 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<List<Integer>> 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<List<Integer>> values = map.values();
for (List<Integer> value : values) {
lists.add(value);
}
return lists;
}
/**
* 代码思想录思路:还是使用双指针法在外面在套一层循环将四数问题转化为3数问题
* 如下所示
* 速度超过49.9%内存超过93.76%
* @param nums
* @param target
* @return
*/
public List<List<Integer>> fourSum1(int[] nums, int target) {
if (nums.length < 4) {
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> 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;
}
}

View File

@ -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:
* 给你四个整数数组 nums1nums2nums3 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<Integer> 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<Integer> 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<Integer, Integer> 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<Integer, Integer> 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<Integer, Integer> 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;
}
}

View File

@ -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 != ji != 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<List<Integer>> threeSum(int[] nums) {
if(nums.length<3){
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> 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 (mid<right){
int sum=nums[left]+nums[mid]+nums[right];
if(sum>0){
right--;
}else if(sum<0){
mid++;
}else {
//sum==0
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(nums[left],nums[mid],nums[right]));
//判断一下左右避免重复
while (mid<right&&nums[mid+1]==nums[mid]) {
mid++;
}
while (mid<right&&nums[right-1]==nums[right]) {
right--;
}
mid++;
right--;
}
}
}
return lists;
}
}