哈希表实际应用 - 设计键
目录
一、设计键
参考文献:https://leetcode-cn.com/leetbook/read/hash-table/xxez6m/
二、字母异位词分组
2.1 题目要求
2.2 解决过程
个人实现
法一:哈希映射。使用 sorted() 将字符串转换为列表并排序,然后使用 string.join() 函数将已排序字符列表重新转换为字符串。从而,对于各字母异位词都能得到相同的中间形式,从而作为 key 存入字典中,value 对应字母异位词原形式。最后,使用列表推导式输出结果。空间复杂度 O(nk),时间复杂度 O(nk)。
2020/08/25 - 98.60% (48ms) - 最佳
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hashmap = {} # dict
for i in range(len(strs)):
s = "".join(sorted(strs[i])) # 字符串排序, 得到中间形式作为 key
if not hashmap.get(s):
hashmap[s] = [strs[i]]
else:
hashmap[s].append(strs[i])
return [j for j in hashmap.values()] # 列表推导式
使用 collections.defaultdict 可以简化书写:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hashmap = collections.defaultdict(list) # defaultdict
for i in range(len(strs)):
s = "".join(sorted(strs[i])) # 字符串排序, 得到中间形式作为 key
hashmap[s].append(strs[i])
return list(hashmap.values()) # 强制类型转换
官方实现与说明
class Solution(object):
def groupAnagrams(self, strs):
ans = collections.defaultdict(list)
for s in strs:
ans[tuple(sorted(s))].append(s)
return list(ans.values())
class Solution:
def groupAnagrams(strs):
ans = collections.defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
ans[tuple(count)].append(s)
return list(ans.values())
其他实现与说明
## 数学方法果然厉害
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103]
hashmap = {}
for s in strs:
key = 1
for c in s:
key *= prime[ord(c) - 97]
if key in hashmap:
hashmap[key].append(s)
else:
hashmap[key] = [s]
return list(hashmap.values())
2020/08/25 - 88.11% (56ms)
参考文献
https://leetcode-cn.com/leetbook/read/hash-table/xxo631/
https://leetcode-cn.com/problems/group-anagrams/solution/zi-mu-yi-wei-ci-fen-zu-by-leetcode/
三、移位字符串分组 (PLUS 会员专享)
四、有效的数独
4.1 题目要求
4.2 解决过程
个人实现
法一:哈希映射 + 哈希集合。为数独表的每一行、每一列、每一块 (index∈[0, 8]) 分别构造一个哈希映射,并存储在 hashmap 中。遍历数独表,第 row 行第 col 列的符号为 board[row][col]。对于有效的数字 (非点号) 而言,通过行号 row 从行键表 row_key 取出键 row_key[row],并于第 row 行的值 —— 哈希集合 hashmap row_key[row] 中检索是否存在重复数字;列号 col 检索列集合同理;块号检索块集合亦复如是。空间复杂度 O(m),m 为数独表中数字数 (0 <= m <= 81);循环遍历次数固定为 9 * 9 = 81 次,各项检索操作设为 O(1),则时间复杂度为 O(1)。
2020/08/27 - 76.13% (52ms)
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
row_key = ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8"] # 行键表
col_key = ["c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8"] # 列键表
#block_key = ["b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8"] # 块键表
hashmap = collections.defaultdict(set) # 辅助哈希映射表
def check_row(row, num):
""" 检查当前数字 num 是否存在于当前行 row """
row_set = hashmap[row_key[row]] # 行集合
if num in row_set:
return True
else:
row_set.add(num)
return False
def check_col(col, num):
""" 检查当前数字 num 是否存在于当前列 col """
col_set = hashmap[col_key[col]] # 列集合
if num in col_set:
return True
else:
col_set.add(num)
return False
def check_block(row, col, num):
""" 检查当前数字 num 是否存在于当前块 block """
if (0 <= row <= 2) and (0 <= col <= 2):
bk = "b0"
elif (0 <= row <= 2) and (3 <= col <= 5):
bk = "b1"
elif (0 <= row <= 2) and (6 <= col <= 8):
bk = "b2"
elif (3 <= row <= 5) and (0 <= col <= 2):
bk = "b3"
elif (3 <= row <= 5) and (3 <= col <= 5):
bk = "b4"
elif (3 <= row <= 5) and (6 <= col <= 8):
bk = "b5"
elif (6 <= row <= 8) and (0 <= col <= 2):
bk = "b6"
elif (6 <= row <= 8) and (3 <= col <= 5):
bk = "b7"
elif (6 <= row <= 8) and (6 <= col <= 8):
bk = "b8"
block_set = hashmap[bk] # 块集合
if num in block_set:
return True
else:
block_set.add(num)
return False
for row in range(9):
for col in range(9):
num = board[row][col] # 当前符号
if (num != ".") and (check_row(row, num) or check_col(col, num) or check_block(row, col, num)):
return False
return True
稍作优化:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
row_key = ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8"] # 行键
col_key = ["c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8"] # 列键
block_key = ["b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8"] # 块键
hashmap = collections.defaultdict(set) # 辅助哈希映射表
def check_row(row, num):
""" 检查当前数字 num 是否存在于当前行 row """
row_set = hashmap[row_key[row]] # 行集合
if num in row_set:
return True
row_set.add(num)
return False
def check_col(col, num):
""" 检查当前数字 num 是否存在于当前列 col """
col_set = hashmap[col_key[col]] # 列集合
if num in col_set:
return True
col_set.add(num)
return False
def check_block(row, col, num):
""" 检查当前数字 num 是否存在于当前块 block """
# 上层块
if (0 <= row <= 2):
if (0 <= col <= 2):
bk = block_key[0]
elif (3 <= col <= 5):
bk = block_key[1]
elif (6 <= col <= 8):
bk = block_key[2]
# 中层块
elif (3 <= row <= 5):
if (0 <= col <= 2):
bk = block_key[3]
elif (3 <= col <= 5):
bk = block_key[4]
elif (6 <= col <= 8):
bk = block_key[5]
# 下层块
elif (6 <= row <= 8):
if (0 <= col <= 2):
bk = block_key[6]
elif (3 <= col <= 5):
bk = block_key[7]
elif (6 <= col <= 8):
bk = block_key[8]
# 检查存在与否
block_set = hashmap[bk] # 块集合
if num in block_set:
return True
block_set.add(num)
return False
# 遍历数独表各位置
for row in range(9):
for col in range(9):
num = board[row][col] # 当前
if (num != ".") and (check_row(row, num) or check_col(col, num) or check_block(row, col, num)):
return False
return True
官方实现与说明
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# init data
rows = [{} for i in range(9)] # 对应 9 行
columns = [{} for i in range(9)] # 对应 9 列
boxes = [{} for i in range(9)] # 对应 9 块
# validate a board
for i in range(9):
for j in range(9):
num = board[i][j] # 当前字符
if num != '.':
num = int(num) # 当前数字
box_index = (i//3)*3 + j//3 ## 块索引转换公式
# keep the current cell value
rows[i][num] = rows[i].get(num, 0) + 1
columns[j][num] = columns[j].get(num, 0) + 1
boxes[box_index][num] = boxes[box_index].get(num, 0) + 1
# check if this value has been already seen before
if rows[i][num] > 1 or columns[j][num] > 1 or boxes[box_index][num] > 1:
return False
return True
2020/08/27 - 76.13% (52ms)
其他实现与说明
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
row = [[0] * 10 for _ in range(9)] # 行数组
col = [[0] * 10 for _ in range(9)] # 列数组
box = [[0] * 10 for _ in range(9)] # 块数组
# 遍历数独表
for i in range(9):
for j in range(9):
# 若为数字
if board[i][j] != '.':
# 当前数字 ASCII 码与 "0" 的 ASCII 码差值作为 index
curNum = ord(board[i][j]) - 48 # ord('0')=48
k = j//3 + (i//3)*3 # block key
# 如果发现已存在
if row[i][curNum] or col[j][curNum] or box[k][curNum]:
return False
# 位置标记
row[i][curNum], col[j][curNum], box[k][curNum] = 1, 1, 1
return True
2020/08/27 - 96.57% (44ms) - 最佳
稍作修改,效率反而低了些:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
row = [set() for _ in range(9)] # 行数组
col = [set() for _ in range(9)] # 列数组
box = [set() for _ in range(9)] # 块数组
# 遍历数独表
for i in range(9):
for j in range(9):
# 若为数字
if board[i][j] != '.':
# 当前数字 ASCII 码与 "0" 的 ASCII 码差值作为 index
curNum = ord(board[i][j]) - 48 # ord('0')=48
k = j//3 + (i//3)*3 # block key
# 如果发现已存在
if (curNum in row[i]) or (curNum in col[j]) or (curNum in box[k]):
return False
# 位置标记
row[i].add(curNum)
col[j].add(curNum)
box[k].add(curNum)
return True
2020/08/27 - 89.73% (48ms) - 说明数量较大时,哈希映射 / 哈希集合 的性能会显著下降 (源于各种冲突)
参考文献
https://leetcode-cn.com/leetbook/read/hash-table/xxpit5/
https://leetcode-cn.com/problems/valid-sudoku/solution/you-xiao-de-shu-du-by-leetcode/
五、寻找重复的子树
5.1 题目要求
5.2 解决过程
测试用例
[0,null,0,0,null,0,0,null,0,null,0,0]
[0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,0]
个人实现
法一:递归 + 遍历。相当于暴力法了,很直观却也很低效的方式。
2020/08/27 - 5.02% (500ms)
class Solution:
def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
# 空树
if not root:
return []
# 线性树 - 单右枝树
elif not root.left:
has_left = False
while root.right:
root = root.right
if root.left:
has_left = True
break
if not has_left:
return []
# 线性树 - 单左枝树
elif not root.right:
has_right= False
while root.left:
root = root.left
if root.right:
has_right = True
break
if not has_right:
return []
def dfs(root, record):
""" 通过 DFS 遍历和记录树节点到哈希映射中 """
if root:
record[root.val].append(root)
dfs(root.left, record)
dfs(root.right, record)
def compare_subtree(root1, root2):
""" 通过递归比较两树是否完全相同 """
if (not root1) and (not root2):
return True
elif ((not root1) or (not root2)) or (root1.val != root2.val):
return False
else:
return compare_subtree(root1.left, root2.left) and compare_subtree(root1.right, root2.right)
hashmap = collections.defaultdict(list)
dfs(root, hashmap)
# 结果列表
result = []
# 遍历树节点值
for key in hashmap.keys():
nodes = hashmap[key]
nodes_num = len(nodes)
# 若值相同的节点不止一个, 则需要进行比较
if nodes_num > 1:
# 遍历值相同的节点
for i in range(nodes_num-1):
done = False # 是否发现过当前重复子树, 默认 False
for j in range(i+1, nodes_num):
# 若待比较节点非空且完全相同
if nodes[i] and nodes[j] and compare_subtree(nodes[i], nodes[j]):
# 若此前尚未发现过重复子树
if not done:
# 则添加相同子树之一到结果列表
result.append(nodes[i])
# 标记置为 True
done = True
# 其余相同子树全部置为 None
nodes[j] = None
return result
官方实现与说明
## 很妙, 第一次见这种解法!
class Solution:
def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
def collect(node):
# 空节点用 "#" 取代
if not node:
return "#"
# 当前子树的序列化结果
serial = "{},{},{}".format(node.val, collect(node.left), collect(node.right))
# 将序列化结果作为键, 使之在哈希计数器中对应的值+1
count[serial] += 1
# 若存在重复的序列化结果, 则说明有重复子树
if count[serial] == 2:
# 将重复字符的根节点加入结果列表中
ans.append(node)
# 返回当前子树序列化结果
return serial
count = collections.Counter() # 哈希计数器
ans = [] # 结果列表
collect(root) # 递归处理
return ans
2020/08/27 - 95.75% (68ms) - 次优
## 更厉害, 也是首次见的解法!
class Solution:
def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
def lookup(node):
if node:
# 当前子树 id = (node.val, left_child_id, right_child_id)
uid = trees[node.val, lookup(node.left), lookup(node.right)]
count[uid] += 1
if count[uid] == 2:
ans.append(node)
return uid
trees = collections.defaultdict() # 默认字典
# 将默认字典的默认值设为当前字典中元素的个数, 从而每个 key 对应的 value 就会是 0, 1, 2, ...
# 如此, UID 将不会有重复
trees.default_factory = trees.__len__
count = collections.Counter() # 哈希计数器
ans = [] # 结果列表
lookup(root)
return ans
2020/08/27 - 95.72% (68ms) - 最佳
参考文献
https://leetcode-cn.com/leetbook/read/hash-table/xxm0i6/
六、设计键 - 总结
参考文献:https://leetcode-cn.com/leetbook/read/hash-table/xxavl2/
本文地址:https://blog.csdn.net/qq_39478403/article/details/108226382