今日题目:
454. 四数相加 II
383. 赎金信
15. 三数之和
18. 四数之和
今日总结
虽然归类在哈希里面,但是重点是使用双指针来去重。三数之和和四数之和的双指针方法务必要掌握。数组遇事不决就排序,排完序记得去重。
454. 四数相加 II
要点
- 没啥难度,学会用哈希来记录数字之和就行
代码:
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. 赎金信
要点:
- 简单题。这里记得当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. 三数之和
要点:
- 今天真正有价值的题目。虽然归在哈希里面,但是实际上需要使用双指针来做实现去重。
- 遇事不决先排序。
- 主要思路就是设置三个指针left mid和right,right设置为尾部,当三数之和小于target则移动mid,大于target则移动right。
- 这题的一大难点在于去重,在排完序后为了避免出现相同的元组,需要跳过重复值,包括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. 四数之和
要点:
- 和三数之和同样的思路。主要难点在于对下标的控制。
- 这题开局的剪枝倒是和三数之和不同,由于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
}