一、概念
哈希表是基于键、值对存储的数据结构,底层一般采用的是列表(数组)。
大家都知道,基于列表(数组)的查询速度非常快,时间复杂度是O(1),常量级别的。
列表的底层存储结构是连续的内存区域,只要给定数据在列表(数组)中的位置,就能直接查询到数据。
哈希表适用于解决的问题:给你一个元素,判断在这个集合是否出现过。
二、 使用哈希表解决两类查找问题
查找有无:元素 ‘a’ 是否存在?可以使用 set 集合这种数据结构。
查找对应关系(键值对应):元素 ‘a’ 出现了几次?可以使用 dict 字典这种数据结构。
2.1集合set的使用
leetcode349.两个数组的交集
解题思路
- 先将数组1存储在哈希表中
- 数组2去遍历查询每一个数组在哈希表里是否出现过
- 如果出现过就把它放进result这个集合中
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
a=set(nums1)
b=set(nums2)
c=a&b
return list(c)
leecode202.快乐数
解题思路
可以用集合 set 来记录之前出现的数字。
- 第 1 部分我们按照题目的要求做数位分离,求平方和。
- 第 2 部分可以使用哈希集合完成。每次生成链中的下一个数字时,我们都会检查它是否已经在哈希集合中。
- 如果它不在哈希集合中,我们应该添加它。
- 如果它在哈希集合中,这意味着我们处于一个循环中,因此应该返回 false。
class Solution:
def isHappy(self, n):
ss = set()
while True:
if n == 1:
return True
total = 0
while n:
total += (n % 10) * (n %10)
n = n // 10
if total in ss:
return False
ss.add(total)
n = total
leetcode217. 存在重复元素
解题思路
- 使用 set 集合存储前面遍历过的元素
- 对于每个元素 num[i],判断是否在前面的 recode 中,如果存在,返回 True。
class Solution:
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
n = len(nums)
if n <= 1:
return False
recode = set()
recode.add(nums[0])
for i in range(1, n):
if nums[i] in recode:
return True
recode.add(nums[i])
return False
解题思路
用字典dict
- 对于每个元素都用哈希表去存储
- 如果遇到元素储存位置已经有元素,则说明遇到相同元素
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
n = len(nums)
if n == 1: return False
dict = {}
for num in nums:
if num in dict:
return True
else: dict[num] = 1
return False
2.2字典dict的使用
字典的格式:字典名 = {key1:value1, key2:value2,…}
定义一个空的字典:my_dict = {} 或 my_dict = dict()
1.两数之和
-enumerate()函数用于将一个可遍历的数据对象(如列表,元组或者字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
// 哈希表
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
return []
常规解法–费时
class Solution(object):
def twoSum(self, nums, target):
for i in range(len(nums)):
for j in range(i+1,len(nums)):
if nums[i] + nums[j] == target:
return [i,j]
leetcode350.两个数组的交集II
解题思路
与leetcode349.两个数组的交集相比,多加了记录元素个数的问题
- 由于同一个数字在两个数组中都可能出现多次,因此需要用哈希表存储每个数字出现的次数。对于一个数字,其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。
- 首先遍历第一个数组,并在哈希表中记录第一个数组中的每个数字以及对应出现的次数
- 遍历第二个数组,对于第二个数组中的每个数字,如果在哈希表中存在这个数字,则将该数字添加到答案,并减少哈希表中该数字出现的次数。
class Solution:
def intersect(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
m = len(nums1)
n = len(nums2)
if m == 0 or n == 0:
return []
dicts = {}
for i in nums1:
if i in dicts:
dicts[i] += 1
else:
dicts[i] = 1
ret = []
for j in nums2:
if j in dicts and dicts[j] > 0:
ret.append(j)
dicts[j] -= 1
return ret
leetcode242.有效的字母异位词
解题思路
- 只要 t 中和 s 中字符都一样且数目都一样,那么就可以了。Python 实现的话可以采用 collections。Counter 这个函数实现对各个字符出现的次数进行统计。
- Counter 是 dict 字典的子类,Counter 拥有类似字典的 key 键和 value 值,只不过 Counter 中的键为待计数的元素,而 value 值为对应元素出现的次数 count。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
return Counter(s)==Counter(t)
leetcode205.同构字符串
解题思路
- 可以使用字典 dict 来记住这些字符对是一个很方便的做法。
- 用了两个字典 dict,一个字典 hashmap 用来记 s 的字符到 t 的映射,另一个字典 ismap 用来记录 t 的字符到 s 的映射
- 判断 t 中的两个字符不能由 s 中同一个字符映射而来。
class Solution:
def isIsomorphic(self, s, t):
hashmap = {}
ismap = {}
for i in range(len(s)):
if s[i] in hashmap:
if hashmap[s[i]] != t[i]:
return False
else:
if t[i] in ismap:
return False
hashmap[s[i]] = t[i]
ismap[t[i]] = True
return True
leetcode387. 字符串中的第一个唯一字符
使用哈希表存储频数
我们可以对字符串进行两次遍历。在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回它的索引,否则在遍历结束后返回 −1。
class Solution:
def firstUniqChar(self, s: str) -> int:
frequency = collections.Counter(s)
for i, ch in enumerate(s):
if frequency[ch] == 1:
return i
return -1
383. 赎金信
比较巧妙的就是使用collections.Counter() 统计字符串中各元素的数量,然后相减进行判断
collections.Counter(ransomNote) - collections.Counter(magazine)
在相减时,该函数默认使用被减数(减号前面的数是被减数,减号后面的数是减数)中含有的元素作为返回结果,对被减数不含有的元素进行忽略,返回结果是{key:value}
value = 被减数对应元素的value - 减数对应元素的value
当对应元素减数(magazine)value > 被减数(ransomNote)value时,返回空值;
最后使用not,not空值返回true, not num(num>0)返回false
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
frequency1=collections.Counter(ransomNote)
frequency2 = collections.Counter(magazine)
if len(ransomNote) > len(magazine):
return False
return not frequency1 - frequency2
451. 根据字符出现频率排序
解题思路
通过一个字典 dict 存储所有字符出现的次数,因为某个字符的出现次数不可能超过 s 的长度,所以我们将每个字符根据其出现次数放入数组中的对应位置,那么最后我们只要从后往前遍历数组所有位置,将不为空的位置的字符串加入结果 ret 中即可。
from collections import Counter
class Solution:
def frequencySort(self, s):
n = len(s)
sc = dict(Counter(s))
ll = ['' for i in range(0, n+1)]
isvisited = set()
ret = ""
for key,value in sc.items():
ll[value] += key * value
for i in range(n, -1, -1):
if ll[i] != '':
ret += ll[i]
return ret;