开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情
作者: 千石
支持:点赞、收藏、评论
欢迎各位在评论区交流
前言
本文内容来自我平时学习的一些积累,如有错误,还请指正
在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题
一些话
本文内容来自我平时学习的一些积累,如有错误,还请指正
在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题
题目练习步骤:
- 给自己10分钟,读题并思考解题思路
- 有了思路以后开始写代码,如果在上一步骤中没有思路则停止思考并且看该题题解
- 在看懂题解(暂时没看懂也没关系)的思路后,背诵默写题解,直至能熟练写出来
- 隔一段时间,再次尝试写这道题目
前置知识
Trie树(字典树)是一种树形结构,常用于存储字符串集合,它以字符串的公共前缀为边。
基本实现: Trie树的每个节点都代表一个字符串的一个前缀,根节点代表空字符串,子节点代表父节点字符串加上一个字符,每个节点通过指针与其他节点相连。
-
常见操作:
- 插入字符串:从根节点开始,依次扫描字符串的每一个字符,如果当前节点的子节点中存在该字符,就转到该子节点;如果不存在该字符的子节点,就创建一个新的子节点。
- 查询字符串:从根节点开始,依次扫描字符串的每一个字符,如果当前节点的子节点中不存在该字符,说明该字符串不存在;如果到达一个节点的末尾,且该节点是一个单词结尾标识,说明该字符串存在。
-
特性:
- 存储效率高:Trie树以公共前缀为边,节省了大量的内存空间。
- 查询效率高:Trie树从根节点开始依次扫描字符,最多只需要扫描几次就能判断字符串是否存在,查询效率非常高。
- 支持前缀查询:Trie树可以通过查询以某个前缀为开头的所有字符串。
- 可以动态插入:Trie树可以动态的插入新的字符串,而不需要重建整棵树。
在实际应用中,Trie树常用于字符串搜索、字典树等场景,由于其优秀的存储和查询性能,是一种非常有效的数据结构。
题目
题目一:212. 单词搜索 II
思路:
使用字典树来存储 words 列表中的所有单词,然后对于二维字符网格 board 中的每个位置,以该位置为起点,使用DFS算法在字典树上进行搜索,如果找到一个单词,将其加入结果列表中。
- 先将所有单词都加入字典树中
- 对于每个单元格,在字典树上查询以该单元格字母为开头的单词,如果存在,则使用并查集判断该单词是否已经被遍历过,如果没有被遍历过,则从该单元格开始向其周围进行深度优先遍历,查询是否能够构成一个单词。
复杂度分析
时间复杂度分析:对于每个单词的查询时间复杂度为O(L),其中L为单词长度,并且每个单词只会被遍历一次,因此总的时间复杂度为O(sum(L))
代码实现
class Node:
def __init__(self, wordId=-1):
self.wordId = wordId
self.next = collections.defaultdict(Node)
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
direct = [(-1, 0), (0, 1), (1, 0), (0, -1)]
r, c, res = len(board), len(board[0]), []
root = Node()
for i, word in enumerate(words):
node = root
for k in word:
node = node.next[k]
node.wordId = i
def dfs(i, j, node):
if i < 0 or i >= r or j < 0 or j >= c\
or board[i][j] not in node.next:
return
tmp, board[i][j] = board[i][j], '$'
node = node.next[tmp]
if node.wordId != -1:
res.append(words[node.wordId])
node.wordId = -1
for x, y in direct:
nx, ny = x + i, y + j
dfs(nx, ny, node)
board[i][j] = tmp
for i in range(r):
for j in range(c):
dfs(i, j, root)
return res
题目二:208. 实现 Trie (前缀树)
代码分析
class Trie:
def __init__(self):
self.root = {}
self.end_of_word = "#"
def insert(self, word: str) -> None:
node = self.root
for char in word:
if char not in node:
node[char] = {}
node = node[char]
node[self.end_of_word] = True
def search(self, word: str) -> bool:
node = self.root
for char in word:
if char not in node:
return False
node = node[char]
return self.end_of_word in node
def startsWith(self, prefix: str) -> bool:
node = self.root
for char in prefix:
if char not in node:
return False
node = node[char]
return True
复杂度分析
时间复杂度:
- insert() 和 search() 方法的时间复杂度都是 O(n),其中 n 是插入的字符串的长度。
- startsWith() 的时间复杂度为 O(n),其中 n 是前缀的长度。
空间复杂度:
- 对于一个长度为 n 的字符串集,Trie 的空间复杂度为 O(26^n),其中 26 是字母表的大小。