Trie 前缀树。
借鉴自 宫水三叶 大佬的 【设计数据结构】实现 Trie (前缀树)。
Trie 树(又叫「前缀树」或「字典树」)是一种用于快速查询「某个字符串/字符前缀」是否存在的数据结构。
其核心是使用「边」来代表有无字符,使用「点」来记录是否为「单词结尾」以及「其后续字符串的字符是什么」。
208. 实现 Trie (前缀树)
实现 Trie 类:
- Trie() 初始化前缀树对象。
- void insert(String word) 向前缀树中插入字符串 word 。
- boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
- boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
总的来说,这三种操作都是三步。
- 第一步从起点开始。
- 第二步遍历字符串,在没有下一个 node 的时候根据方法的不同进行不同的操作,insert 创造新结点,search return False,startsWith return False,然后要更新 node。
- 第三步根据方法的返回结果或进行操作,insert 指出这个最后的 node 是叶结点、search 返回当前 node 是不是叶结点,startsWith 返回当前 node 是不是空的。
class Trie:
def __init__(self):
N = 100009
self.index = 1
self.ends = [0] * N
self.trie = [[0] * 26 for _ in range(N)]
def insert(self, word: str) -> None:
node = 0
for c in word:
i = ord(c) - ord('a')
if self.trie[node][i] == 0:
self.trie[node][i] = self.index
self.index += 1
node = self.trie[node][i]
self.ends[node] += 1
def search(self, word: str) -> bool:
node = 0
for c in word:
i = ord(c) - ord('a')
if self.trie[node][i] == 0: return False
node = self.trie[node][i]
return self.ends[node] != 0
def startsWith(self, prefix: str) -> bool:
node = 0
for c in prefix:
i = ord(c) - ord('a')
if self.trie[node][i] == 0: return False
node = self.trie[node][i]
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)开始直接创造十万个 node……之后连接。或者可以下面这样模拟结点。
class Trie:
def __init__(self):
self.children = [None] * 26
self.end = False
def insert(self, word: str) -> None:
node = self
for c in word:
c = ord(c) - ord('a')
if not node.children[c]: node.children[c] = Trie()
node = node.children[c]
node.end = True
def search(self, word: str) -> bool:
node = self
for c in word:
c = ord(c) - ord('a')
if not node.children[c]: return False
node = node.children[c]
return node.end
def startsWith(self, prefix: str) -> bool:
node = self
for c in prefix:
c = ord(c) - ord('a')
if not node.children[c]: return False
node = node.children[c]
return True if node else False
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)211. 添加与搜索单词 - 数据结构设计
使用 Trie 前缀树,在 search 的时候需要一小部分 dfs。
class WordDictionary:
def __init__(self):
self.children = [None] * 26
self.end = False
def addWord(self, word: str) -> None:
node = self
for c in word:
c = ord(c) - ord('a')
if not node.children[c]: node.children[c] = WordDictionary()
node = node.children[c]
node.end = True
def search(self, word: str) -> bool:
if not word: return self.end
if word[0] == '.':
for i in range(26):
if self.children[i]:
if self.children[i].search(word[1:]): return True
else:
i = ord(word[0]) - ord('a')
if self.children[i]: return self.children[i].search(word[1:])
return False
# Your WordDictionary object will be instantiated and called as such:
# obj = WordDictionary()
# obj.addWord(word)
# param_2 = obj.search(word)212. 单词搜索 II
第一反应不知道怎么做,听说可以用 Trie 之后第二反应是遍历 board 能够组成的字符串把它们都放到 Trie 里,这样肯定存不下。换个思路需要把 words 都存到 Trie 里。
- 要注意 dfs 传入的参数,不仅有当前形成的字符串,还有当前走到了哪一个结点。
- board 中可能有不止一个满足相同的 word,所以一开始使用 set 自动去重。
# @lc code=start
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
class Trie:
def __init__(self):
self.children = [None] * 26
self.end = False
def insert(self, word):
node = self
for c in word:
c = ord(c) - ord('a')
if not node.children[c]: node.children[c] = Trie()
node = node.children[c]
node.end = True
m, n = len(board), len(board[0])
root = Trie()
ans = set()
for word in words:
root.insert(word)
def dfs(x, y, node, cur):
c = board[x][y]
if c == '.': return
node = node.children[ord(c) - ord('a')]
if not node: return
if node.end:
ans.add(cur + c)
board[x][y] = '.'
for x1, y1 in ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)):
if 0 <= x1 < m and 0 <= y1 < n:
dfs(x1, y1, node, cur + c)
board[x][y] = c
for x in range(m):
for y in range(n):
dfs(x, y, root, "")
return list(ans)