208. 实现 Trie (前缀树)
问题描述
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 104
次
解题思路与代码实现
主要思路:
根据前缀树的概念实现,查找和加入类似于二叉搜索树。
class Trie {
// 根节点
private Node root;
public Trie() {
root = new Node();
}
/**
* 插入字符串
* 非递归写法
*/
public void insert(String word) {
if (word != null && word.length() > 0) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
// 不包含该字符c
if (!cur.childrenMap.containsKey(c)) {
Node node = new Node();
cur.childrenMap.put(c, node);
}
cur = cur.childrenMap.get(c);
}
if (!cur.isWord) { // 标记为单词
cur.isWord = true;
}
}
}
/**
* 查找单词
*/
public boolean search(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
// 在字典树中逐个字符向下查找
if (cur.childrenMap.containsKey(c)) {
cur = cur.childrenMap.get(c);
} else {
return false;
}
}
return cur.isWord;
}
/**
* 查找是否存在指定前缀
*/
public boolean startsWith(String prefix) {
Node cur = root;
for (int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
// 在字典树中逐个字符向下查找
if (cur.childrenMap.containsKey(c)) {
cur = cur.childrenMap.get(c);
} else {
return false;
}
}
return true;
}
}
class Node {
public boolean isWord; // 是否为单词
public HashMap<Character, Node> childrenMap; // key当前节点的字符,value到下一个节点的映射
public Node() {
childrenMap = new HashMap<>();
}
}
踩坑点
无
212. 单词搜索 II
问题描述
给定一个 m x n
二维字符网格 board
和一个单词(字符串)列表 words
, 返回所有二维网格上的单词 。
单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例 1:
输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]
示例 2:
输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j]
是一个小写英文字母1 <= words.length <= 3 * 104
1 <= words[i].length <= 10
words[i]
由小写英文字母组成words
中的所有字符串互不相同
解题思路与代码实现
主要思路:
首先根据words
构造字典树,然后通过DFS+回溯
找到board矩阵中出现在字典树中的字符串,用HashSet收集避免重复
。
class Solution {
public static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public HashSet<String> set = new HashSet<>(); // 记录board数组能够产生的所有字符串
public Trie trie = new Trie(); // 构建字典树用于查找
public List<String> findWords(char[][] board, String[] words) {
// 构建字典树
for (String word : words) {
trie.insert(word);
}
// 遍历board数组
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
// 以board[i][j]为起点,搜索所有可能的字符串
boolean[][] visited = new boolean[board.length][board[0].length]; // 标记数组
dfs(board, new StringBuilder(), visited, i, j, trie.root);
}
}
// 收集words数组中的字符串在字典树中的部分
return new ArrayList<>(set);
}
/**
* dfs找出以board[i][j]开头的字符串
* 这种结构确保每次递归执行后都会回溯
*/
public void dfs(char[][] board, StringBuilder builder, boolean[][] visited,
int x, int y, Node node) {
// 越界或者已访问过的跳过
if (x < 0 || x >= board.length ||
y < 0 || y >= board[0].length || visited[x][y]) {
return;
}
visited[x][y] = true; // 标记已访问
builder.append(board[x][y]);
Node childNode = node.childrenMap.get(board[x][y]);
// 如果能向下查找ch字符
if (childNode != null) {
if (childNode.isWord) { // 如果是单词,加入到结果集中
set.add(builder.toString());
}
// 向四周搜索
for (int[] dir : DIRECTIONS) {
dfs(board, builder, visited, x + dir[0], y + dir[1], childNode); // 递归
}
}
// 回溯
builder.deleteCharAt(builder.length() - 1);
visited[x][y] = false;
}
}
class Trie {
// 根节点
public Node root;
public Trie() {
root = new Node();
}
/**
* 插入字符串
*/
public void insert(String word) {
if (word != null && word.length() > 0) {
Node cur = root;
for (char c : word.toCharArray()) {
if (!cur.childrenMap.containsKey(c)) {
cur.childrenMap.put(c, new Node());
}
cur = cur.childrenMap.get(c);
}
cur.isWord = true; // 标记为单词
}
}
/**
* 查找单词
*/
public boolean search(String word) {
Node cur = root;
for (char c : word.toCharArray()) {
if (cur.childrenMap.containsKey(c)) {
cur = cur.childrenMap.get(c);
} else {
return false;
}
}
return cur.isWord;
}
}
class Node {
public boolean isWord; // 是否为单词
public HashMap<Character, Node> childrenMap; // key当前节点的字符,value到下一个节点的映射
public Node() {
childrenMap = new HashMap<>();
}
}
踩坑点
无