【力扣100】简要总结之哈希

发布于:2025-03-20 ⋅ 阅读:(14) ⋅ 点赞:(0)


1、两数之和

思路

使用map,【键】:元素值 【值】:下标
遍历原值,找对应的值是否存在于map中
若存在,返回两者下标
若不存在,将当前值与下标存入map

代码(Go)

func twoSum(nums []int, target int) []int {
	m := make(map[int]int) // 创建一个 map,存储已经遍历过的数值及其索引
	for index, val := range nums {
		if preindex, ok := m[target-val]; ok { // 查找 map 中键为 target-val 的值是否存在
			return []int{index, preindex} // 找到匹配的索引对,返回
		} else {
			m[val] = index // 如果没有匹配的,存入 map
		}
	}
	return []int{} // 没找到,返回空切片
}

// map中的存储结构为 {key:数据元素,value:数组元素对应的下标}

2、字母异位词分组

(1)计数法

思路

由于字母异位词的字符串包含相同的字母,且每个字母的出现次数相同,可以用一个数组来记录每个字母的出现次数
例如,字符串 “eat” 和 “tea”,字母出现次数是相同的:{e: 1, a: 1, t: 1}。
这样我们可以用一个固定大小的数组(长度为 26,表示字母 ‘a’ 到 ‘z’)作为哈希表的键。

实现

1,遍历每个字符串,创建一个大小为 26 的数组,用来记录每个字母的出现次数。
2,用该数组作为哈希表的,把原字符串添加到对应的键下。
3,最后遍历哈希表,得到所有的字母异位词组。

代码(Go)

// 计数法
func groupAnagrams(strs []string) [][]string {
	mp := map[[26]int][]string{} // {}表示初始化一个空的map  mp:将字母异位词分组的临时数据结构
	for _, str := range strs {
		cnt := [26]int{} // 即mp的键
		for _, b := range str {
			cnt[b-'a']++
		}
		mp[cnt] = append(mp[cnt], str) // 更新map中cnt键对应的值(字符串切片)
	}
	ans := make([][]string, 0, len(mp)) // [][]string相当于c的string[][]  len(mp) 是总的字母异位词组数
	for _, v := range mp {              // 返回键、值
		ans = append(ans, v)
	}
	return ans
}

(2)排序法

思路

字母异位词的字符串可以通过排序变成一样的字符串。比如 “eat” 和 “tea” 排序后都是 “aet”。
因此,可以将每个字符串排序后作为键(key)来存储在哈希表中。所有排序后相同的字符串,属于同一个字母异位词组。

实现

1,遍历字符串数组,将每个字符串转换成排序后的字符串。
2,使用排序后的字符串作为哈希表的,把原字符串添加到对应的键下。
3,最后遍历哈希表,得到所有的字母异位词组。

代码(Go)

// 排序法
func groupAnagrams(strs []string) [][]string {
	mp := make(map[string][]string) // 创建一个哈希表,键是排序后的字符串,值是字母异位词组
	for _, str := range strs {
		s := []byte(str)                    // 将字符串转为字节【切片】,是复合类型
		sort.Slice(s, func(i, j int) bool { // 对字节切片进行排序
			return s[i] < s[j] //这个规定的其实就是,大的在前还是小的在前
		})
		sortedStr := string(s)                     // 将排序后的字节切片转换回字符串
		mp[sortedStr] = append(mp[sortedStr], str) // 将原字符串加入到对应的字母异位词组
	}

	ans := make([][]string, 0, len(mp))
	for _, v := range mp {
		ans = append(ans, v)
	}
	return ans
}

(3)比较

方法一(排序法):通过排序字符串作为哈希表的键。每个字符串的排序时间复杂度是 O(k log k),因此总时间复杂度是 O(n * k log k)。
方法二(计数法)【性能更好】:通过记录字母出现次数的数组作为哈希表的键。每个字符串的处理时间是 O(k),因此总时间复杂度是 O(n * k)。

3、最长连续序列

思路

题目要求找到数组中的 最长连续子序列
给定数组没有排序,且可能包含重复的元素,需要去重
可以用哈希表来查找数字是否存在
【哈希表自动去重,因为它的键是唯一的,不会存储重复的数字】

实现

1,哈希表存储: 将数组中的每个数字存入一个哈希表 numSet,以便能够快速查找每个数字是否存在。
2,遍历数组: 对于每个数字,检查它是否是一个潜在的连续序列的起点。即它的前一个数字 num-1 不存在于哈希表中。如果是起点,就从该数字开始,继续查找连续的数字。
3,更新最长子序列: 每次找到一个连续子序列时,更新当前找到的最长子序列的长度。

代码(Go)

func longestConsecutive(nums []int) int {
	numSet := map[int]bool{}           // 创建一个哈希表 numSet,用来存储数组中的所有数字,键是数字,值是 bool 类型,用来标记数字是否存在
	for _, num := range nums {         // 遍历 nums 数组,将每个数字存入哈希表 numSet
		numSet[num] = true             // 由于哈希表的键是唯一的,重复的数字会自动去重
	}
	maxLen := 0
	for num := range numSet {          // 通过 range 获取哈希表中的每个键(即数字)
		if !numSet[num-1] {            // 只有 num-1 不存在,才是连续序列的起点
			currentNum := num          // 从当前 num 开始,向上查找连续的数字
			currentMax := 1            // 初始化当前连续序列的长度为 1(包含当前的 num)
			for numSet[currentNum+1] { // 相当于while, 为 true 时,说明当前数字的下一个数字存在
				currentNum++           // 递增,继续查找下一个数字
				currentMax++
			}
			if currentMax > maxLen {   // 如果找到的连续子序列长度大于当前最长的长度,则更新最长长度
				maxLen = currentMax
			}
		}
	}
	return maxLen
}