字典树及经典题目
文章目录
一、字典树Trie
(空间换时间)
- 优点:最大限度的减少无谓的字符串比较,查询效率比哈希表高;
- 使用场景:统计和排序大量的字符串。经常被搜索引擎用于文本词频统计;
- 特性:
- 节点不存完整单词;
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串;
- 不同路径代表的字符串都不相同;
- 节点上添加数值,可以代表统计频次,对用户进行推荐;
二、经典题目
2.1 208. 实现 Trie (前缀树)
Java实现:
class Trie {
private Trie[] t;
private static final int R = 26;
private boolean end;
public Trie() {
t = new Trie[R];
}
public void insert(String word) {
Trie x = this;
for (int i=0; i<word.length(); i++) {
if (!x.containsTrie(word.charAt(i))) {
x.putNode(word.charAt(i), new Trie());
}
x = x.getNode(word.charAt(i));
}
x.setEnd();
}
public boolean search(String word) {
Trie p = searchPrefix(word);
return p!=null && p.isEnd();
}
public boolean startsWith(String prefix) {
Trie p = searchPrefix(prefix);
return p!=null;
}
private Trie searchPrefix(String prefix) {
Trie x = this;
for (int i=0; i<prefix.length(); i++) {
if (x.containsTrie(prefix.charAt(i))) {
x = x.getNode(prefix.charAt(i));
}else {
return null;
}
}
return x;
}
private boolean containsTrie(char c) {
return getNode(c)!=null;
}
private Trie getNode(char c) {
return t[c-'a'];
}
private void putNode(char c, Trie trie) {
t[c-'a'] = trie;
}
private boolean isEnd() {
return end;
}
private void setEnd() {
this.end = true;
}
}
python的实现
class Trie:
def __init__(self):
self.root = {}
self.end_of_word = '#'
def insert(self, word: str) -> None:
m = self.root
for w in word:
m = m.setdefault(w, {})
m[self.end_of_word] = {}
def search(self, word: str) -> bool:
m = self.root
for w in word:
if w in m:
m = m[w]
else:
return False
return self.end_of_word in m
def startsWith(self, prefix: str) -> bool:
m = self.root
for w in prefix:
if w in m:
m = m[w]
else:
return False
return True
2.2 212. 单词搜索 II
private int[] dx = {1, -1, 0, 0};
private int[] dy = {0, 0, 1, -1};
public List<String> findWords(char[][] board, String[] words) {
Trie trie = new Trie();
for (String word: words) {
trie.insert(word);
}
Set<String> resList = new HashSet<>();
StringBuilder sb = new StringBuilder();
for (int i=0; i<board.length; i++) {
for (int j=0; j<board[i].length; j++) {
sb.append(board[i][j]);
char c = board[i][j];
board[i][j] = '#';
dfs(board, i, j, trie, sb, resList);
sb.deleteCharAt(sb.length()-1);
board[i][j] = c;
}
}
return new ArrayList<>(resList);
}
private void dfs(char[][] board, int i, int j, Trie trie, StringBuilder sb, Set<String> resList) {
if (!trie.findPrefix(sb.toString())) {
return;
}
if (trie.findWord(sb.toString())) {
resList.add(sb.toString());
}
for (int k=0; k<dx.length; k++) {
int x = i+dx[k], y = j + dy[k];
if (x<0 || x>=board.length || y<0 || y>=board[0].length || board[x][y]=='#') {
continue;
}
sb.append(board[x][y]);
char c = board[x][y];
board[x][y] = '#';
dfs(board, x, y, trie, sb, resList);
sb.deleteCharAt(sb.length()-1);
board[x][y] = c;
}
}
private class Trie{
private Trie[] tries;
private static final int R = 26;
private boolean end;
public Trie() {
tries = new Trie[R];
}
public void insert(String word) {
Trie x = this;
for (int i=0; i<word.length(); i++) {
if (!x.containsTrie(word.charAt(i))) {
x.setTrie(word.charAt(i), new Trie());
}
x = x.getTrie(word.charAt(i));
}
x.setEnd();
}
public boolean findPrefix(String prefix) {
return searchPrefix(prefix)!=null;
}
public boolean findWord(String word) {
Trie t = searchPrefix(word);
return t!=null && t.isEnd();
}
private Trie searchPrefix(String prefix) {
Trie x = this;
for (int i=0; i<prefix.length(); i++) {
if (x.containsTrie(prefix.charAt(i))) {
x = x.getTrie(prefix.charAt(i));
}else {
return null;
}
}
return x;
}
private boolean isEnd() {
return end;
}
private void setEnd() {
end = true;
}
private void setTrie(char c, Trie node) {
tries[c-'a'] = node;
}
private boolean containsTrie(char c) {
return getTrie(c)!=null;
}
private Trie getTrie(char c) {
return tries[c-'a'];
}
}
2.3 79. 单词搜索 , DFS
private int[] dx = {0, 0, 1, -1};
private int[] dy = {1, -1, 0, 0};
public boolean exist(char[][] board, String word) {
// https://leetcode-cn.com/problems/word-search/
StringBuilder sb = new StringBuilder();
for (int i=0; i<board.length; i++) {
for (int j=0; j<board[0].length; j++) {
sb.append(board[i][j]);
char c = board[i][j];
board[i][j] = '#';
boolean res = dfs(i, j, board, sb, word);
board[i][j] = c;
sb.deleteCharAt(sb.length()-1);
if (res) {
return res;
}
}
}
return false;
}
private boolean dfs(int i, int j, char[][] board, StringBuilder sb, String word) {
if (!word.startsWith(sb.toString())) {
return false;
}
if (word.equals(sb.toString())) {
return true;
}
for (int k=0; k<dx.length; k++) {
int x = i + dx[k], y = j + dy[k];
if (x<0 || x>=board.length || y<0 || y>=board[0].length || board[x][y]=='#') {
continue;
}
sb.append(board[x][y]);
char c = board[x][y];
board[x][y] = '#';
boolean res = dfs(x, y, board, sb, word);
board[x][y] = c;
sb.deleteCharAt(sb.length()-1);
if (res) {
return res;
}
}
return false;
}
本文介绍了字典树(Trie)数据结构及其优势,如空间换时间提高查询效率,常用于搜索引擎的词频统计。文章详细解析了LeetCode上的三道相关题目:208.实现Trie、212.单词搜索II和79.单词搜索,分别展示了Java和Python的实现,并结合深度优先搜索(DFS)解决实际问题。
420

被折叠的 条评论
为什么被折叠?



