代码随想录 day07 第三章 哈希表part02

今日任务

●  454.四数相加II

●  383. 赎金信

●  15. 三数之和

●  18. 四数之和

●  总结

1. 四数相加II

关联 leetcode 454.四数相加II

  • 思路

    • 目标是找到满足累加和的所有组合
    • 每个数组里面挑选一个数满足累加 == 0 就行
    • 两两配对,求和
    • 因为都是单向循环,所以能保证在每个数组内都只取一个数
    • Golang: map特性 默认零值,这题不用判断key是否出现过
  • 题解

    • 使用哈希表 map形式 实现
    func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
    	//满足要求的 a+b+c+d = 0 的次数
    	ret := 0
    
    	// 存放数组1,2的累加和与出现次数
    	// key: 所取两个元素之和, val: 出现的次数
    	// Golang: map特性 默认零值,这题不用判断key是否出现过
    	sumMap := make(map[int]int)
    	for i := range nums1 {
    		for j := range nums2 {
    			idx := nums1[i] + nums2[j]
    			sumMap[idx]++
    		}
    	}
    	// 遍历数组3, 4求和
    	// 从已遍历过的map中找到满足条件的 数组1,2 的和
    	// 不需要移除map元素
    	for i := range nums3 {
    		for j := range nums4 {
    			idx := 0 - (nums3[i] + nums4[j])
    			ret += sumMap[idx]
    		}
    	}
    	return ret
    }
    

2. 赎金信

关联 leetcode 383. 赎金信

  • 思路:
    • magazine里字母出现的次数是否包含了ransomNote所需要的所有字母。
    • magazine里面的每个字母只能用一次
    • map可以解决但空间消耗大于数组
  • 题解
    • Map

      func canConstruct(ransomNote string, magazine string) bool {
      	alphaMap := make(map[rune]int)
      
      	for _, chart := range magazine {
      		alphaMap[chart]++
      	}
      	for _, chart := range ransomNote {
      		alphaMap[chart]--
      		if alphaMap[chart] < 0 {
      			return false
      		}
      	}
      	return true
      }
      
    • Array

      func canConstruct(ransomNote string, magazine string) bool {
      	alphaArray := make([]int, 26)
      
      	for _, chart := range magazine {
      		alphaArray[chart-'a']++
      	}
      	for _, chart := range ransomNote {
      		alphaArray[chart-'a']--
      		if alphaArray[chart-'a'] < 0 {
      			return false
      		}
      	}
      	return true
      }
      

3. 三数之和

关联 leetcode 15. 三数之和

  • 这道题哈希表反而比较麻烦,建议用双指针解决

  • 感觉本题的描述不太清楚重新按照自己的语言组织一下

    • 要求返回的数组内 三个数字之和为0
    • 三个数字在原数组内的下标不同
    • 每三个数构成一个集合的元素
      • 每一组三个数仅能出现一次
        • 所以也就出现了剪枝的需求
  • 题解

    func threeSum(nums []int) [][]int {
    	rets := make([][]int, 0)
    	//1. 先对原数组排序
    	slices.Sort(nums)
    
    	for i := 0; i < len(nums)-2; i++ {
    		if nums[i] > 0 {
    			//排序后的第一个元素都大于0,则后续所有元素都>0; 直接返回就好
    			return rets
    		}
    
    		// 对元素a去重, 前面已经算过该元素了
    		// 当前数组: -1,-1,0,1,1
    		// i 在 左二的 -1, 就是之前已经计算过了
    		if i > 0 && nums[i] == nums[i-1] {
    			continue
    		}
    		//左右双指针, 往中间移动
    		l := i + 1         // 左边元素
    		r := len(nums) - 1 //右边元素, 当前数组最大元素
    
    		for r > l {
    			if nums[i]+nums[l]+nums[r] > 0 {
    				// 和 > 0, 缩小右值
    				r--
    			} else if nums[i]+nums[l]+nums[r] < 0 {
    				// 和 < 0, 增大左值
    				l++
    			} else {
    				// 和为0,记录结果
    				rets = append(rets, []int{nums[i], nums[l], nums[r]})
    				//右指针剪枝
    				for r > l && nums[r] == nums[r-1] {
    					//解释:
    					//例如: 此时满足的元素 -1
    					//当前数组: -1,-1,0,1,1
    					//现在右指针在最右边的1, 则从右二的1也不能出现在结果里面,因为数值重复了
    					r-- // 移除重复右元素
    				}
    				//左指针同理
    				for r > l && nums[l] == nums[l+1] {
    					l++ // 移除重复左元素
    				}
    
    				// 找到答案时,双指针同时收缩
    				r--
    				l++
    
    			}
    		}
    	}
    
    	return rets
    }
    

4. 四数之和

关联 leetcode 18. 四数之和

  • 类似三数之和

    • 在三数之和外面再加一个指针
    • 最外层两个指针【紧挨着】,一同从头向尾移动【保证了两个 index 不同的数】
    • 同三数之和一样继续做剪枝去重操作
  • target的要求,target不同于三数之和可以为负数

  • 结果要求 [][]int{}

    • 里面的 []int{nums[k],nums[i],nums[l],nums[r]}
      • k, i, l, r 互不相等
      • nums[k],nums[i],nums[l],nums[r] 彼此间可以相等
  • 题解

    func fourSum(nums []int, target int) [][]int {
    	var rets [][]int
    	//1. 依旧先对数组排序
    	slices.Sort(nums)
    	for k := range nums {
    		//剪枝
    		if nums[k] > target && nums[k] >= 0 {
    			break
    		}
    		//去重
    		if k > 0 && nums[k] == nums[k-1] {
    			continue
    		}
    		for i := k + 1; i < len(nums)-2; i++ {
    			//剪枝
    			if nums[k]+nums[i] > target && nums[k]+nums[i] >= 0 {
    				break
    			}
    			//去重
    			if i > k+1 && nums[i] == nums[i-1] {
    				continue
    			}
    			//	通三数之和的双指针
    			l, r := i+1, len(nums)-1
    			for r > l {
    				if nums[k]+nums[i]+nums[l]+nums[r] > target {
    					r--
    				} else if nums[k]+nums[i]+nums[l]+nums[r] < target {
    					l++
    				} else {
    					rets = append(rets, []int{nums[k], nums[i], nums[l], nums[r]})
    					//	去重
    					for l < r && nums[l] == nums[l+1] {
    						l++
    					}
    					for l < r && nums[r] == nums[r-1] {
    						r--
    					}
    					//	收缩左右指针
    					l++
    					r--
    				}
    			}
    		}
    	}
    	return rets
    }
    

5. 总结

  • 一般来说哈希表都是用来快速判断一个元素是否出现集合里
  • 全部元素都是 小写字母
    • 固定范围用数组
  • 哈希表可以用来检索当前元素是否出现过
  • 用哈希表来去重比较困难

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值