哈希表-17/20
设计哈希表-0/2
设计哈希集合
设计哈希映射
哈希集合应用-4
存在重复元素,简单
用set秒杀
只出现一次的数字,简单
用set秒杀,单空间复杂度是O(n)
方法二:异或运算空间复杂度O(1),相同的数异或得0,所有数异或得答案
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ans = 0
for item in nums:
ans ^= item
return ans
两个数组的交集,简单
直接内置秒杀
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = set(nums1)
set2 = set(nums2)
return set1&set2
#或者写
return set.intersection(set1,set2)
快乐数,简单
因为可能会无限循环,所以用一个set存所有已经出现过的迭代结果,如果出现重复则证明无限循环了
class Solution:
def trans(self,n):
s = str(n)
lst = list(s)
ans = 0
for item in lst:
ans += int(item)**2
return ans
def isHappy(self, n: int) -> bool:
numset = set()
while n not in numset and n != 1:
numset.add(n)
n = self.trans(n)
return n == 1
方法二:用快慢指针检测,如果快指针先到1了,则true,如果快慢指针相遇,则false
哈希映射应用-6
其实就是字典
两数之和,简单
先排序,再用双指针,需要用字典记住排序之前的下标(如果列表里有重复数字怎么办?)
官解:
查 target - item 是否已经在字典里,如果在,返回这两个下标,如果不在,把 item 加入字典
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i ,item in enumerate(nums):
if target - item in dic:
return [dic[target - item],i]
else:
dic[item] = i
同构字符串,简单
用哈希表对应 s 和 t 的字符,要注意,“ab” “bb”也是不对的,所以要用一个 set 来检查 t 里的字符有没有重复使用
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
if len(s) != len(t):return False
n = len(s)
dic = {}
used = set()
for i in range(n):
if s[i] not in dic:
if t[i] not in used:#加上了这个判断
dic[s[i]] = t[i]
used.add(t[i])
else:
return False
else:
if dic[s[i]] != t[i]:
return False
return True
两个列表的最小索引总和,简单
用minsum存最小索引和,ans列表存答案的字符串
class Solution:
def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
minsum = 2000
set2 = set(list2)
for i,item in enumerate(list1):
if item in list2:#这里如果用in set2用时会短一些
temp = i + list2.index(item)
if temp < minsum:
minsum = temp
ans = [item]
elif temp == minsum:
ans.append(item)
return ans
字符串中的第一个唯一字符,简单
遍历两遍s,第一遍修改字典,第二遍查找字典键对应的值是否等于1
字典,键位字符,值为出现次数
class Solution:
def firstUniqChar(self, s: str) -> int:
dic = {}
n = len(s)
for item in s:
if item not in dic:
dic[item] = 1
else:
dic[item] += 1
for i in range(n):
if dic[s[i]] == 1:
return i
return -1
两个数的交集II,简单
遍历两遍字典,用字典记录列表1或2中数字在两个列表之中的个数,取min
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
if len(nums1) > len(nums2):#加上这句可以优化空间
return self.intersect(nums2,nums1)
dic = {}
ans = []
for item in nums1:
if item not in dic:
dic[item] = [1,0]
else:
dic[item][0] += 1
for item in nums2:
if item in dic:
dic[item][1] += 1
for item in dic:
k = min(dic[item][0],dic[item][1])
for _ in range(k):
ans.append(item)
return ans
存在重复元素II,简单
暴力超时
哈希解法一:字典键值对为 “数字:最近下标”,检测到重复数字时,如果当前下标与最近下标超过3,则更新最近下标,否则返回True
哈希解法二:维护一个大小为 k 的散列表,在遍历时,如果在散列表中查到,则true。但是Python字典内的存储顺序是随机的,所以没法通过字典操作删除k个item中加入字典最早的。
法一代码:
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
dic = {}
for i,item in enumerate(nums):
if item not in dic:
dic[item] = i
else:
if i - dic[item] > k:
dic[item] = i
else:
return True
return False
设计键-3
字母异位词分组,中等
注意:用sorted排序字符串会返回排序后的字符列表,需要join链接成字符串
class Solution:
def sortstr(self,s):
lst = sorted(s)
return ''.join(lst)
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
count = -1
ans = []
dic = {}
for item in strs:
temp = self.sortstr(item)
if temp not in dic:
count += 1
dic[temp] = count
ans.append([item])
else:
ans[dic[temp]].append(item)
return ans
有效的数独,中等
给每行每列每3x3小块都设置一个set
小块的编号 = 3*(行号//3) + (列号//3)
记得把 ‘.’ 跳过
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rowset = [set() for _ in range(9)]
colset = [set() for _ in range(9)]
subset = [set() for _ in range(9)]
for i in range(9):
for j in range(9):
subnum = 3*(i//3) + j//3
num = board[i][j]
if num == '.':
continue
if num not in rowset[i] and num not in colset[j] and num not in subset[subnum]:
rowset[i].add(num)
colset[j].add(num)
subset[subnum].add(num)
else:
return False
return True
寻找重复的子树,中等
官解:用一个三元组来唯一标识一个子树 (node.val , left id , right id ),相同的 id 表示子树相同
则叶节点的 id 为 ( node.val , None , None )
直接将三元组作为key,count作为值的运行时间
远大于
先将三元组映射到 UID = int ,再用UID 索引 count
故使用了两个字典,一个字典将 三元组映射给一个与之对应的独一无二的 uid , 另一个字典用 uid 作为 key 来维护其出现的次数
class Solution:
def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
trees = {}
count = {}
ans = []
def getuid(node):
if node:
tempkey = (node.val,getuid(node.left),getuid(node.right))
if tempkey not in trees:
trees[tempkey] = len(trees)
#用已经加入字典的个数来作为 uid,保证了uid 的唯一性
uid = trees[tempkey]
if uid not in count:
count[uid] = 1
else:
count[uid] += 1
if count[uid] == 2:
ans.append(node)
return uid
getuid(root)
return ans
小结-4/5
宝石与石头,简单
把 J 哈希,然后遍历 S
Python中 in 的时间复杂度:set、dic 为 O(1),list 为 O(n)
class Solution:
def numJewelsInStones(self, J: str, S: str) -> int:
jewset = set(J)
count = 0
for item in S:
if item in jewset:#这两句在LeetCode里运行速度其实差不太多,波动都很大
#if item in J:
count += 1
return count
无重复字符的最长子串,中等
跟官解思路一致:双指针法,用一个 set 来存当前无重复字符串里的字符
右指针先遍历,遍历到set里没有的,加入set,
遍历到有的,左指针从当前位置开始遍历到重复元素上次出现的位置,将路上的元素删除
遍历的过程中计算当前无重复字符串的长度
注意:先添加/移除元素,再修改 left 和 right 的值
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:return 0
uniq = set()
n = len(s)
ans ,tempans ,left ,right = 0, 0, 0, 0
while right < n:
if s[right] not in uniq:
#right += 1
uniq.add(s[right])
right += 1
tempans += 1
#把这一句判断移到else后面,可以减少比较次数,但是需要在所有遍历结束后补上一次判断
if tempans > ans:
ans = tempans
else:
while s[left] != s[right]:
#left += 1
uniq.remove(s[left])
left += 1
tempans -= 1
left += 1 #这一句漏了
right += 1
return ans
四数相加 II,中等
分成两组处理,遍历AB,将 A[i] + B[j] 出现的次数存入以 A[i] + B[j] 为键的字典
遍历CD,在dic中检索 -(C[i] + D[j]) ,如果找到了,则答案 += dic[ -(C[i] + D[j])]
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
dic = {}
n = len(A)
count = 0
for i in range(n):
for j in range(n):
num = A[i] + B[j]
if num in dic:
dic[num] += 1
else:
dic[num] = 1
for i in range(n):
for j in range(n):
num = C[i] + D[j]
if -num in dic:
count += dic[-num]
return count
前 K 个高频元素,中等
用哈希表统计每个元素出现的次数,O(n)
直接对次数排序,O(nlogn),不符合要求
维护一个 k 个元素的最小堆,O(nlogk),符合要求
在这里插入代码片
常数时间插入、删除和获取随机元素,中等
用 list 存元素的值,用 dic 存元素在 list 中的索引。
添加:加list 末尾,O(1)
删除:在dic 中查找 val 的索引 k,将 list中索引 k 的值与list 末尾值为val1的元素交换,将dic 中val1的索引修改为k,删除 list 末尾元素O(1),删除 dic 中索引 val的条目O(1)
随机:随机返回list的一个值,用 random.choice(list)
class RandomizedSet:
def __init__(self):
self.store = []
self.index = {}
self.size = 0
def insert(self, val: int) -> bool:
if val in self.index:
return False
self.store.append(val)
self.index[val] = self.size
self.size += 1
return True
def remove(self, val: int) -> bool:
if val not in self.index:
return False
k = self.index[val]
self.store[k] = self.store[-1]
self.index[self.store.pop()] = k
self.index.pop(val)
self.size -= 1
return True
def getRandom(self) -> int:
return choice(self.store)