代码随想录训练营day07|哈希表part02

454. 四数相加II

题目链接:454. 四数相加 II

这个题目和两数之和的做法非常相似,将前两个变量和后两个变量合并,就和两数之和非常相似。此外,由于本题是在四个数组中做四数相加,因此不需要考虑重复的情况。所以,这里直接考虑使用哈希表来解决这个问题,将A+B的和存入map中,再判断target-(C+D)的值在map中是否存在相应的key,如果存在,需要加上该key的数量(value)

func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {

	map1 := make(map[int]int)
	total := 0
	for i := 0; i < len(nums1); i++ {
		for j := 0; j < len(nums2); j++ {
			sum := nums1[i] + nums2[j]
			map1[sum]++
		}
	}

	for i := 0; i < len(nums3); i++ {
		for j := 0; j < len(nums4); j++ {
			if _, ok := map1[0-(nums3[i]+nums4[j])]; ok {
				total += map1[0-(nums3[i]+nums4[j])]
			}
		}
	}

	return total
}

383. 赎金信

题目链接:383. 赎金信

这个题目基本没有什么难度,就是判断构成字符串A的字符集合是否是构成字符串B的字符集合的子集。乍一看,直接把字符串B的字符和对应数量当作键值对存在map中,再使用循环遍历字符串A的所有字符,对其进行两个判断:1)当前字符是否存在map中;2)map中当前字符的数量是否足够。

使用hash能够很好的解决该问题,但是我们不能无脑使用哈希表,毕竟哈希表需要耗费大量空间,我们可以针对具体的问题使用数组进行模拟,因为数组具有一个和哈希表一样的特点,就是存取数据所耗费的时间复杂度都是O(1)。因此,根据对题观察,我们发现英文字符的数量是存在上限的,所有直接使用数组模拟map也能很好的解决问题。

func canConstruct(ransomNote string, magazine string) bool {

	map1 := make(map[string]int)

	magazine_s := []rune(magazine)
	ransomNote_s := []rune(ransomNote)
	for _, n := range magazine_s {
		map1[string(n)]++
	}

	for _, m := range ransomNote_s {
		if _, ok := map1[string(m)]; !ok {
			return false
		} else {
			if map1[string(m)] > 0 {
				map1[string(m)]--
			} else {
				return false
			}
		}
	}

	return true
}

下面的使用数组的解法代码来源:代码随想录

func canConstruct(ransomNote string, magazine string) bool {
    record := make([]int, 26)
    for _, v := range magazine {   // 通过record数据记录 magazine里各个字符出现次数
        record[v-'a']++
    }
    for _, v := range ransomNote { // 遍历ransomNote,在record里对应的字符个数做--操作
        record[v-'a']--
        if record[v-'a'] < 0 {     // 如果小于零说明ransomNote里出现的字符,magazine没有
            return false
        }
    }
    return true
}

15. 三数之和

题目链接:15. 三数之和

这道题由于惯性思想,直接套用前面四数相加II的想法,无脑套用map来解决,后来发现使用map得出的解会出现很多的重复解,而且这个去重的方法没有想出来,查看代码随想录的代码也没有看懂,所以直接学习代码随想录给出的双指针解法

核心思想可以分为以下几个步骤:

对于A+B+C=0

1、对数组进行排序,这样方便后续利用数组的元素的相对位置信息

2、第一层循环nums[i]用来固定A的位置,去寻找后面BC的位置

3、利用leftright指针,分别指向A的下一个位置和数组的尾部位置

4、第二层循环,限定right > left,当找到一个符合条件的三元组时,两个指针同时移动,否则根据和大于0还是小于0来移动rightleft

上面四个步骤只是大概方法,还有几个细节需要注意:

1、去重

对于A的去重,放在第一层循环里,判断当前位置的元素是否等于上一个元素

		// 对第一个元素进行去重,不能使用nums[i] == nums[i+1],否则会错过[-1,-1,2]这组数据
		if i > 0 && nums[i] == nums[i-1] {
			continue
		}

对于BC的去重,则是需要在找到了相应的解之后,再进行去重,否则会遗漏一部分解,例如[0,0,0,0]

				res = append(res, []int{nums[i], nums[left], nums[right]})
				//停在重复元素的最后一个
				for right > left && nums[left] == nums[left+1] {
					left++
				}
				// 停在重复元素的最前一个
				for right > left && nums[right] == nums[right-1] {
					right--
				}

2、剪枝

通过剪枝操作来减少搜索的时间。根据本题,由于数组经过排序,如果固定位置的A处的值大于0那么就没有必要进行搜索了,直接返回即可。这个剪枝操作需要根据题目内容来设计,但是这个思想能够很好的帮主我们减少程序不必要的运行时间。

func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	res := [][]int{}

	if nums[0] > 0 {
		return res
	}

	for i := 0; i < len(nums); i++ {
		if nums[i] > 0 {
			return res
		}
		// 对第一个元素进行去重,不能使用nums[i] == nums[i+1],否则会错过[-1,-1,2]这组数据
		if i > 0 && nums[i] == nums[i-1] {
			continue
		}
		left := i + 1
		right := len(nums) - 1

		for right > left {
			if nums[i]+nums[left]+nums[right] > 0 {
				right--
			} else if nums[i]+nums[left]+nums[right] < 0 {
				left++
			} else {
				res = append(res, []int{nums[i], nums[left], nums[right]})
				//停在重复元素的最后一个
				for right > left && nums[left] == nums[left+1] {
					left++
				}
				// 停在重复元素的最前一个
				for right > left && nums[right] == nums[right-1] {
					right--
				}
				right--
				left++
			}
		}

	}
	return res

}

18. 四数之和

题目来源:18. 四数之和

这个题目和前面三数之和的做法是一样的,但是一些细节方面的处理略微有些不同。

初始思路:和三数之和一样,找固定位置,由于这是四个数,需要利用双指针寻找的话,就得固定前两个位置也就是AB,CD的搜索同样使用leftright指针的模式,然后对A进行剪枝和去重操作。

上面思路写完代码运行之后,发现还有重复的解。后续查看卡哥的思路才发现,我们对AB都做了固定位置的操作,但是只是对A做了去重操作,并没有对B进行去重操作,这样会导致在B位置出现重复值。例如[2,2,2,2,2],target=8. 如果不对B去重,那么会有两个解,分别对应两个位置【0,1,2,4】和【0,2,3,4】。

因此这个地方对B也需要进行去重操作,以此类推,五数之和、六数之和都是如此

去重

			if j > i+1 && nums[j] == nums[j-1] {
				continue
			}

剪枝

对于B的剪枝原理上和A相同,和三数之和又有所不同,三数之和题目里taget是固定的,等于0,而本题中target是动态变化的,因此不能简单的通过一个当前固定值大于0就停止搜索.

1、对于A的剪枝,需要考虑nums[A]大于target的同时,还需要判断nums[A]>0 || target > 0,防止出现target=-10nums[A]=-4,直接跳过搜索。

2、对于B的剪枝,则需要在A的基础上,考虑一下nums[A]+nums[B]>0即可

func fourSum(nums []int, target int) [][]int {
	sort.Ints(nums)
	res := [][]int{}
	for i := 0; i < len(nums); i++ {
		//减枝,提高速度,避免不必要的判断
		// 前面nums[i] > 0使用用于过滤target<0的情况
		if nums[i] > target && (nums[i] > 0 || target > 0) {
			return res
		}
		if i > 0 && nums[i] == nums[i-1] {
			continue
		}
		for j := i + 1; j < len(nums); j++ {
			if nums[i]+nums[j] > target && (nums[j] > 0 || target > 0) {
				break
			}
			if j > i+1 && nums[j] == nums[j-1] {
				continue
			}

			left := j + 1
			right := len(nums) - 1
			for right > left {
				//这里不能先去重,要等当前符合条件的四元组加入res之后再去判断重复,否则会漏掉四元组

				if nums[i]+nums[j]+nums[left]+nums[right] > target {
					right--
				} else if nums[i]+nums[j]+nums[left]+nums[right] < target {
					left++
				} else {

					res = append(res, []int{nums[i], nums[j], nums[left], nums[right]})
					for right > left && nums[left] == nums[left+1] {
						left++
					}
					for right > left && nums[right] == nums[right-1] {
						right--
					}
					left++
					right--

				}
			}

		}
	}
	return res

}

哈希表总结

关于哈希表的实现方式,在Go语言当中,官方的库里只有数组和map的实现方式,并没有set,只有第三方库的set实现,这是我后续需要去学习的地方。

其次,需要在做题时需要分析何时使用数组,何时使用map。要依据两者数据结构的特点来选择相应的实现方式

最后,对于Go语言中Map的底层实现原理,后续需要学习,在此插个眼,学习完之后在这儿总结一下。

Go之Map底层原理学习资源:Go Map底层实现原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值