今日任务
● 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] 彼此间可以相等
- 里面的 []int{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. 总结
- 一般来说哈希表都是用来快速判断一个元素是否出现集合里
- 全部元素都是 小写字母
- 固定范围用数组
- 哈希表可以用来检索当前元素是否出现过
- 用哈希表来去重比较困难