454.四数相加II
哈希
先算前两数之和,再算后两数之和,再转化为两数之和的做法
时间复杂度O(n2) 空间复杂度O(n)
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
// 先暴力求出前两数和后两数之和,再哈希查找,时间复杂度O(n2),注意和相同问题
sumList := make([]int, 0) // 存放前两数的和
sumMap := make(map[int]int) // 存放后两数和的哈希表
result := 0 // 结果
for _, n1 := range nums1 {
for _, n2 := range nums2 {
sumList = append(sumList, n1+n2)
}
}
for _, n3 := range nums3 {
for _, n4 := range nums4 {
sumMap[n3 + n4]++
}
}
for _, n := range sumList{
result += sumMap[-n]
}
return result
}
383.赎金信
数组
使用数组来存放字母出现的频率
时间复杂度O(n) 空间复杂度O(1)
func canConstruct(ransomNote string, magazine string) bool {
// 法一:数组存储字母出现频率,后者频率大于前者即为true
charList := [26]int{} // 26个字母
for _, char := range magazine{
charList[char - 'a']++
}
for _, char := range ransomNote{
charList[char - 'a']--
if charList[char - 'a'] < 0 {
return false
}
}
return true
}
哈希
将出现的字符的频率用哈希表存起来,解决任意字符的问题
func canConstruct(ransomNote string, magazine string) bool {
// 法二:将出现的字符频率用哈希表存起来,解决任意字符的问题
charMap := map[rune]int{}
for _, r := range magazine {
charMap[r]++
}
for _, r := range ransomNote {
charMap[r]--
if charMap[r] < 0 {
return false
}
}
return true
}
15.三数之和
遍历+哈希
先遍历数组,将数字出现的频率用map存起来,再遍历两数之和,判断第三个数是否存在(实现方式多了很多重复的判断,故有些慢)
时间复杂度 O(n2) 空间复杂度 O(n)
func threeSum(nums []int) [][]int {
// 先遍历数组,将元素出现次数存入哈希表中
countMap := make(map[int]int) // 记录元素出现次数
resultMap := make(map[[3]int]bool) // 存放是否出现过
result := make([][]int, 0)
for _, num := range nums {
countMap[num]++
}
for i := 0; i < len(nums)-2; i++ {
for j := i + 1; j < len(nums)-1; j++ {
// 判断元素
target := -nums[i] - nums[j]
tempArr := [3]int{nums[i], nums[j], target}
for ii := 0; ii < 3; ii++ {
for kk := 0; kk < 3-ii-1; kk++ {
if tempArr[kk] > tempArr[kk+1] {
tempArr[kk], tempArr[kk+1] = tempArr[kk+1], tempArr[kk]
}
}
}
countMap[nums[i]]--
countMap[nums[j]]--
if countMap[target] > 0 && resultMap[tempArr] == false {
resultMap[tempArr] = true
result = append(result, []int{tempArr[0], tempArr[1], tempArr[2]})
}
countMap[nums[i]]++
countMap[nums[j]]++
}
}
return result
}
排序+哈希
先进行排序,再处理,可以节省一些遍历。因为排序之后第三个数只能出现在右侧,且代码上容易写判断逻辑
时间复杂度 O(n2) 空间复杂度 O(n)
func threeSum(nums []int) [][]int {
// 先排序,减少不必要的遍历
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j] // 从小到大排序
})
countMap := make(map[int]int) // 记录该数字是否存在,且索引值在哪
for index, num := range nums {
countMap[num] = index
}
result := make([][]int, 0)
isAppear := make(map[[3]int]bool) // 记录是否已经出现过
for i := 0; i < len(nums) - 2; i++ {
for j := i + 1; j < len(nums) - 1; j++ {
target := -nums[i] - nums[j]
if countMap[target] > j {
if _, ok := isAppear[[3]int{nums[i], nums[j], target}]; !ok {
isAppear[[3]int{nums[i], nums[j], target}] = true
result = append(result, []int{nums[i], nums[j], target})
}
}
}
}
return result
}
18.四数之和
排序+哈希
和上面一题相同的解法,多套了一层for循环
时间复杂度 O(n3) 空间复杂度O(n)
func fourSum(nums []int, target int) [][]int {
// 同样先排序,再遍历三个数
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j] // 升序
})
indexMap := make(map[int]int) // 存放某个数字最后出现时的索引
isAppear := make(map[[4]int]bool)
result := make([][]int, 0)
for index, val := range nums {
indexMap[val] = index
}
for i := 0; i < len(nums) - 3; i++ {
for j := i + 1; j < len(nums) - 2; j++ {
for k := j + 1; k < len(nums) - 1; k++ {
fourNum := target - nums[i] - nums[j] - nums[k]
if indexMap[fourNum] > k{
if _, ok := isAppear[[...]int{nums[i], nums[j], nums[k], fourNum}]; !ok{
isAppear[[...]int{nums[i], nums[j], nums[k], fourNum}] = true
result = append(result, []int{nums[i], nums[j], nums[k], fourNum})
}
}
}
}
}
return result
}