哈希表理论基础
哈希表(Hash Table/Hash Map)
A hash table uses a hash function on an element to compute an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. During lookup, the key is hashed and the resulting hash indicates where the corresponding value is stored.
哈希函数(Hash Function)
index = hash_function(value),通过这个函数,我们能在O(1)的时间判断value是否在表中
哈希碰撞(collision)
- 由于hash function或者哈希表大小的原因,我们可能会map不同的值到同一个index下,我们叫这种现象哈希碰撞。
- 一般哈希碰撞有两种解决方法, 拉链法(Separate chaining)和线性探测法(Open addressing/linear probing)。
拉链法
- 冲突的元素放在同一个index下,并用链表连接
- 要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
- 平均查找时间O(1)
线性探测法
- 使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
- 如果碰撞发生,我们根据某种确定的规则,找到下一个空位放下
常见的三种哈希结构
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set (集合)
- map(映射)
总结
- 当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
- 但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
242. 有效的字母异位词
用dict来数每个字符出现的个数
# Time complexity: O(N)
# Space complexity: O(|Z|) 最多26个字母
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
alphabet_dict = {}
for char_s in s:
alphabet_dict[char_s] = alphabet_dict.get(char_s, 0) + 1
for char_t in t:
if char_t not in alphabet_dict or alphabet_dict[char_t] == 0:
return False
alphabet_dict[char_t] -= 1
return True
用list来数每个字符出现的个数
# Time complexity: O(N)
# Space complexity: O(|Z|) Z是含有26个字母的字典集
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
alphabet_list = [0] * 26
for char_s in s:
alphabet_list[ord(char_s)-ord('a')] += 1
for char_t in t:
if alphabet_list[ord(char_t)-ord('a')] == 0:
return False
alphabet_list[ord(char_t)-ord('a')] -= 1
return True
排序比较
# Time complexity: O(NlogN)
# Space complexity: O(1)
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
return sorted(s) == sorted(t)
349. 两个数组的交集
利用set的intersection方法
# Time complexity : O(n+m) in the average case and O(n×m) in the worst case when load factor is high enough.
# Space complexity : O(n+m) in the worst case when all elements in the arrays are different.
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return set(nums1).intersection(set(nums2))
利用set去重,然后找交集
# Time complexity : O(n+m)
# Space complexity : O(n+m) in the worst case when all elements in the arrays are different.
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set_nums1 = set(nums1)
set_nums2 = set(nums2)
if len(set_nums1) < len(set_nums2):
return [x for x in set_nums1 if x in set_nums2]
return [x for x in set_nums2 if x in set_nums1]
利用list做哈希表
# Time complexity : O(n+m)
# Space complexity : O(1) 固定长度数组
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
count = [0] * 1000
res = set()
for num_1 in nums1:
count[num_1] = 1
for num_2 in nums2:
if count[num_2] == 1:
res.add(num_2)
return [*res]
202. 快乐数
使用set记录是否重复出现相同的sum
- 题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要! 重复出现就return False, 我们用set来记录已经出现过的数字
# Time complexity: O(logN)
# Space complexity: O(logN)
class Solution:
def isHappy(self, n: int) -> bool:
def sum_list(nums):
res = 0
for num in nums:
res += num**2
return res
digits = [int(x) for x in str(n)]
res = sum_list(digits)
res_set = set()
while res != 1:
digits = [int(x) for x in str(res)]
res = sum_list(digits)
if res in res_set:
return False
res_set.add(res)
return True
# another way to get the digits of n
class Solution:
def isHappy(self, n: int) -> bool:
def cal_sum(n):
res = 0
while n:
res += (n % 10) ** 2
n = n // 10
return res
seen = set()
while True:
n = cal_sum(n)
if n == 1:
return True
if n in seen:
return False
seen.add(n)
1. 两数之和
利用dict来记录出现数字对应的index,一次遍历数组完成查找
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
index_dict = {}
for i, num in enumerate(nums):
remain = target - num
if remain in index_dict:
return [i, index_dict[remain]]
index_dict[num] = i