代码随想录 | Day07 | 2023.12.19 | 哈希02

今日题目:

454. 四数相加 II
383. 赎金信
15. 三数之和
18. 四数之和

今日总结

虽然归类在哈希里面,但是重点是使用双指针来去重。三数之和和四数之和的双指针方法务必要掌握。数组遇事不决就排序,排完序记得去重。

454. 四数相加 II

要点

  1. 没啥难度,学会用哈希来记录数字之和就行

代码:

func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
    hash := make(map[int]int)
    result := 0
    for _, v1 := range nums1 {
        for _, v2 := range nums2 {
            hash[v1+v2]++
        }
    }
    for _, v3 := range nums3 {
        for _, v4 := range nums4 {
            if value, exists :=hash[-(v3+v4)]; exists {
                result += value
            }
        }
    }
    return result
}

383. 赎金信

要点:

  1. 简单题。这里记得当hash小于0时直接跳出就行了
func canConstruct(ransomNote string, magazine string) bool {
    if len(ransomNote) > len(magazine) {
        return false
    }
    hash := make(map[rune]int)
    for _,v := range magazine {
        hash[v]++
    }    
    for _,v := range ransomNote {
        hash[v]--
        if hash[v] < 0 {
            return false
        }
    }    
    return true
}

15. 三数之和

要点:

  1. 今天真正有价值的题目。虽然归在哈希里面,但是实际上需要使用双指针来做实现去重。
  2. 遇事不决先排序。
  3. 主要思路就是设置三个指针left mid和right,right设置为尾部,当三数之和小于target则移动mid,大于target则移动right。
  4. 这题的一大难点在于去重,在排完序后为了避免出现相同的元组,需要跳过重复值,包括left也需要。要注意对齐。
func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	if len(nums) < 3 || nums[0] > 0 {
		return nil
	}
	result := make([][]int, 0)

	for left := 0; left < len(nums)-2; left++ {
		if left > 0 && nums[left] == nums[left-1] { // 跳过 left 的重复值
			continue
		}
		mid, right := left+1, len(nums)-1
		for mid < right {
			curSum := nums[left] + nums[mid] + nums[right]
			if curSum == 0 {
				result = append(result, []int{nums[left], nums[mid], nums[right]})
				for mid < right && nums[mid] == nums[mid+1] { // 跳过 mid 的重复值
					mid++
				}
				for mid < right && nums[right] == nums[right-1] { // 跳过 right 的重复值
					right--
				}
				mid++    //这两行用于对齐,这样才算是选取了不同的元素
				right--
			} else if curSum > 0 {
				right--
			} else {
				mid++
			}
		}
	}
	return result
}

15. 四数之和

要点:

  1. 和三数之和同样的思路。主要难点在于对下标的控制。
  2. 这题开局的剪枝倒是和三数之和不同,由于target并非是0,排序后不能直接根据首元素大于target而直接return,因为target可能是负数。
func fourSum(nums []int, target int) [][]int {
    if len(nums) < 4 {
        return nil
    }
    sort.Ints(nums)
    if nums[0] > target && target >=0 {
        return nil
    }
    result := make([][]int, 0)

    for left := 0; left < len(nums)-3; left++ {
        if left > 0 && nums[left] == nums[left-1] {   //left 和 midLeft也需要去重
            continue // 跳过重复的数字
        }
        for midLeft := left + 1; midLeft < len(nums)-2; midLeft++ {
            if midLeft > left+1 && nums[midLeft] == nums[midLeft-1] {
                continue // 跳过重复的数字
            }
            midRight := midLeft + 1
            right := len(nums) - 1
            for midRight < right {
                curSum := nums[left] + nums[midLeft] + nums[midRight] + nums[right]
                if curSum == target {
                    result = append(result, []int{nums[left], nums[midLeft], nums[midRight], nums[right]})
                    // 去重
                    for midRight < right && nums[midRight] == nums[midRight+1] {
                        midRight++
                    }
                    for midRight < right && nums[right] == nums[right-1] {
                        right--
                    }
                    midRight++ // 对齐
                    right--
                } else if curSum < target {
                    midRight++
                } else {
                    right--
                }
            }
        }
    }
    return result
}
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值