哈希表
什么是哈希表?
哈希表也叫做散列表,是根据关键码的值直接进行访问的数据结构。
直白来讲,数组就是一个哈希表。哈希表的关键码就对应的是数组的索引下标,然后通过下标访问数组中的元素。
一般哈希表都用来快速判断一个元素是否出现在集合里。
下面直接来做题体验一下。
242. 有效的字母异位词
给定两个字符串 *s*
和 *t*
,编写一个函数来判断 *t*
是否是 *s*
的字母异位词。
注意:若 *s*
和 *t*
中每个字符出现的次数都相同,则称 *s*
和 *t*
互为字母异位词。
解题思路
这道题较为简单,我的第一个想法就是利用python中的sorted函数对两个字符串进行排序,此时排序后的两个字符串一定是按照字母大小排序的新字符串,如果相同就说明s和t是字母异位词,否则则不是。代码如下。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
s = sorted(s)
t = sorted(t)
if s == t:
return True
else:
return False
从哈希表的角度来考虑这道题则是将字符串中的每个字母都统计出其出现的次数,可以用数组或者字典来实现。如下:
1.数组实现
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
hash_s = [0]*26 #定义一个哈希表数组,用于统计每个字母的出现次数
hash_t = [0]*26
for char in s:
char_index = ord(char) - ord('a')#减去a的ASCII码,得到0-25的对应关系
hash_s[char_index] += 1
for char in t:
char_index = ord(char) - ord('a')#减去a的ASCII码,得到0-25的对应关系
hash_t[char_index] += 1
return hash_s == hash_t
tips:ord()
函数用于返回字符的ASCII码,此处用于字符到数组下标索引的映射。
2.字典实现
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
hash_s = {} #定义一个字典用于统计每个词出现的次数
hash_t = {}
for char in s:
hash_s[char] = hash_s.get(char,0) + 1
for char in t:
hash_t[char] = hash_t.get(char,0) + 1
return hash_s == hash_t
tips:字典的 get()
函数的作用是从字典中获取某个键的值,例如上述代码中hash_s[char] = hash_s.get(char,0) + 1
,是统计char字符的值,如果字典中不存在char这个键的话,则新增这个键并赋值为0;如果存在,则为当前的值。
383. 赎金信
给你两个字符串:ransomNote
和 magazine
,判断 ransomNote
能不能由 magazine
里面的字符构成。
如果可以,返回 true
;否则返回 false
。
magazine
中的每个字符只能在 ransomNote
中使用一次。
解题思路
同样,这道题可以用哈希表数组来解,具体思路为用两个长度为26的数组分别代表两个字符串的各种字母的数量,然后依次比较相同字母的数量大小,当magazine
里的字母数量都大于ransomNote
时,说明满足题目要求,否则不满足。代码如下:
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
hash_r = [0]*26 #直接用数组映射
hash_m = [0]*26
for char in ransomNote:
hash_r[ord(char)-ord('a')] += 1
for char in magazine:
hash_m[ord(char)-ord('a')] += 1
for i in range(26):
if hash_r[i] > hash_m[i]:
return False
return True
49. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
解题思路
这个题与前面题目略有不同,主要区别在于需要判别的字母异位词变多了,而且不是固定的数。这种情况下再想用之前映射到数组的方法就较为麻烦。
那如何对相同的字母异位词分类呢?他们有什么共同之处?
还记得第一道题中采用sorted函数的方法吗?这个方法就是发现了字母异位词的相同之处,就是排序之后的新字符串相同。因此我们可以将列表中的每个字符串都进行一次排序,并以排序后的新字符串作为字典中的键。那么如果排序后的字符串相同,就说明互为字母异位词;如果不同,则在字典中新建一个键。代码如下:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hash_dic = {} #设一个字典存储同一类的词汇
for word in strs:
sorted_word = ''.join(sorted(word))
if sorted_word in hash_dic:
hash_dic[sorted_word].append(word)
else:
hash_dic[sorted_word] = [word]
return list(hash_dic.values())
tips:sorted()
函数是将字符串从小到大排序,排序结果是以列表形式呈现的,因此需要用到''.join(sorted(word))
将排序后的独立字符连接成字符串。''
两个单引号间表示字符串的间隔符,此处为空即为每个字符间不需要间隔。
438. 找到字符串中所有字母异位词
给定两个字符串 s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
解题思路
这道题本来还想用上个题的sorted函数来解决的,结果超出时间限制了。。。sorted函数太花时间了。这里要注意。
既然sorted方法走不通,就换一种思路,这道题使用滚动窗口法是毫无疑问能行得通的,只需要在s字符串中以p字符串的长度滚动即可。那么怎么省下时间呢?
可以采用哈希表数组来实现,以空间换时间,思路为:每次循环中都统计一下窗口中两个字符串的每个词出现的次数,看是否相同,如果相同则为字母异位词,保存索引到列表中;若不同,则继续循环。操作完成后,窗口向后移动,移动时要注意,窗口是整体向后移动的,因此需要加上新字母减去旧字母。代码实现如下:
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
l_s, l_p, ans = len(s), len(p), []
if l_s < l_p: return ans
s_char = [0]*26
p_char = [0]*26
for i in range(l_p):
p_char[ord(p[i])-ord('a')] += 1
s_char[ord(s[i])-ord('a')] += 1
if p_char == s_char:
ans.append(0)
for i in range(l_p,l_s):
s_char[ord(s[i-l_p])-ord('a')] -= 1
s_char[ord(s[i])-ord('a')] += 1
if s_char == p_char:
ans.append(i-l_p+1)
return ans