开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情
作者: 千石
支持:点赞、收藏、评论
欢迎各位在评论区交流
前言
本文大纲
一些话
本文内容来自我平时学习的一些积累,如有错误,还请指正
在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题
题目练习步骤:
- 给自己10分钟,读题并思考解题思路
- 有了思路以后开始写代码,如果在上一步骤中没有思路则停止思考并且看该题题解
- 在看懂题解(暂时没看懂也没关系)的思路后,背诵默写题解,直至能熟练写出来
- 隔一段时间,再次尝试写这道题目
正文
前置知识
-
哈希表:
- 实现:通过哈希函数将键映射到桶,存储键值对,具体存储形式可以是链表或者开放寻址法。
- 特性:查询快,平均时间复杂度O(1);但当哈希冲突较多时,性能会下降,最坏时间复杂度O(n)。
-
映射:
- 实现:通过某种数据结构,如二分搜索树,红黑树,线段树等,存储键值对,并可以根据键值查询对应的值。
- 特性:查询效率和插入效率都较高,平均时间复杂度O(logn)。
-
集合:
- 实现:通过某种数据结构,如二分搜索树,平衡树,位运算等,存储一组元素,并且元素不能重复。
- 特性:元素不重复,插入效率和删除效率都较高,平均时间复杂度O(logn)。
解题小窍门
-
哈希表:
- 快速的查找一个元素是否在表中,也可以方便的计数。
- 如果数据是字符串或者数字,那么哈希表可以快速的对数据进行分类。
- 用来去重。
-
映射:
- 快速的查找一个元素的值,以及更新元素的值。
- 用映射来统计每一个元素出现的次数。
-
集合:
- 快速的查找一个元素是否在集合中,也可以快速的插入一个元素。
- 解决问题中的并集、交集等关系。
- 用来去重,也可以用来判断两个集合的交集是否为空。
实战
题目一:242. 有效的字母异位词 - 力扣(LeetCode)
做题前可以思考
如果题目没有给出明确的限制条件
- 思考异位词的具体含义
- 考虑是否会有大小写敏感
解题思路
- 暴力法
首先将两个字符串转换为列表,并对其中每一个字符进行排序,然后再将两个列表比较,如果相同,则说明是字母异位词,否则不是。
复杂度分析:
因为使用了 sorted() 函数,该函数的复杂度为 O(nlogn)。此外,比较字符的复杂度为 O(n),因此整个算法的复杂度为 O(nlogn + n) = O(nlogn)。
- 哈希表
使用哈希表,统计每一个字符在字符串 s 和 t 中出现的频次,如果最终两个哈希表相同,则说明两个字符串是字母异位词。
复杂度分析:
因为使用了哈希表,在每次字符的查找和修改操作的复杂度为 O(1),因此整个算法的复杂度为 O(n)。
代码实现
- 暴力法
def isAnagram(s, t):
if len(s) != len(t):
return False
s = sorted(s)
t = sorted(t)
for i in range(len(s)):
if s[i] != t[i]:
return False
return True
- 哈希表
def isAnagram(s, t):
if len(s) != len(t):
return False
hashmap = {}
for i in s:
if i not in hashmap:
hashmap[i] = 1
else:
hashmap[i] += 1
for i in t:
if i not in hashmap:
return False
else:
hashmap[i] -= 1
for i in hashmap:
if hashmap[i] != 0:
return False
return True
题目二:49. 字母异位词分组 - 力扣(LeetCode)
解题思路
- 排序+Hash表
对每个字符串排序,使用排序后的字符串作为key,将其原始字符串存储在Hash表中,如果排序后的字符串已经存在,将原始字符串加入该key对应的list中。最后,将所有的value值拼接起来,组成答案。
复杂度分析:
- 排序操作:O(nlogn)
- Hash表插入操作:O(n)
- 整体复杂度:O(nlogn + n) = O(nlogn)
- 计数+Hash表
对每个字符串的每个字符,计数其出现的次数,使用计数的结果作为key,将其原始字符串存储在Hash表中,如果计数的结果已经存在,将原始字符串加入该key对应的list中。最后,将所有的value值拼接起来,组成答案。
复杂度分析:
- 计数操作:O(n)
- Hash表插入操作:O(n)
- 整体复杂度:O(n + n) = O(n)
代码实现
- 排序+Hash表
from collections import defaultdict
def groupAnagrams(strs):
dic = defaultdict(list)
for s in strs:
dic[''.join(sorted(s))].append(s)
return list(dic.values())
- 计数+Hash表
from collections import defaultdict
def groupAnagrams(strs):
dic = defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
dic[tuple(count)].append(s)
return list(dic.values())
题目三:Loading Question... - 力扣(LeetCode)
解题思路
这道题目在之前的文章中涉及过,这里讲一种新的思路:哈希表
使用哈希表,存储每个数的下标,遍历数组,判断target减去当前数是否存在,若存在,则该数与target减去该数即为答案。
复杂度分析: 遍历数组的时间复杂度为O(n),查询哈希表的时间复杂度为O(1),故总的时间复杂度为O(n)。
代码实现
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i]
dic[nums[i]] = i
代码解释
代码实现的步骤如下:
- 声明哈希表 dic,存储每个数的下标。
- 遍历数组 nums,对于每个数字 nums[i],判断 target - nums[i] 是否在哈希表 dic 中,若存在,说明当前数字与 target - nums[i] 相加等于 target。
- 返回当前数字与 target - nums[i] 在数组中的下标,即为答案。
- 若不存在,则将当前数字 nums[i] 和下标存入哈希表 dic 中。
Tips:代码中需要注意的是,哈希表 dic 的键存储的是数字,值存储的是下标,方便在第二步判断。
总结
本文介绍了哈希表、映射、集合的实现、特性和使用时的小技巧,并且介绍了几道题目,希望能帮助读者更好的理解三者