字典树 Krains 2020-09-01
时间:2022-07-24
本文章向大家介绍字典树 Krains 2020-09-01,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
应用
- 搜索引擎的自动补全
- 拼写检查
当然还有其他的数据结构,如哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)
时间内寻找键值,却无法高效的完成以下操作: 找到具有同一前缀的全部键值。
Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)
,其中 n
是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间, 查找键值Trie 树只需要 O(m)
的时间复杂度,其中 m
为键长。
定义字典树数据结构
// 字典树数据结构,isEnd标记当前结点是否为一个单词的末尾,即表示该路径下是不是一个完整的单词
// 用map存储下一个字符和其对应的结点,字典树的根不表示任何字符
class TireNode {
private boolean isEnd = false;
private Map<Character, TireNode> subNode = new HashMap<>();
public boolean isEnd() {
return isEnd;
}
public void setEnd() {
isEnd = true;
}
public TireNode getChild(Character c) {
return subNode.get(c);
}
public void addChild(Character c, TireNode node) {
subNode.put(c, node);
}
}
一些常用的字典树有关方法
class Trie{
private TireNode root = new TireNode();
// 没有处理word为空串或者为空字符""的情况
// Insert a word into the tire.
public void insert(String word) {
TireNode temp = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (temp.getChild(c) == null) {
temp.addChild(c, new TireNode());
}
temp = temp.getChild(c);
// 标记叶子结点
if (i == word.length() - 1)
temp.setEnd();
}
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
TireNode temp = root;
for(int i = 0; i < word.length(); i++){
char c = word.charAt(i);
temp = temp.getChild(c);
if(temp == null){
return false;
}
}
return temp.isEnd();
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
TireNode temp = root;
for(int i = 0; i < prefix.length(); i++){
char c = prefix.charAt(i);
temp = temp.getChild(c);
if(temp == null){
return false;
}
}
return true;
}
}
经典例题
212. 单词搜索 II
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。
示例:
输入:
words = ["oath","pea","eat","rain"] and board =
[
['o','a','a','n'],
['e','t','a','e'],
['i','h','k','r'],
['i','f','l','v']
]
输出: ["eat","oath"]
涉及到匹配多个单词,用字典树。
将words存入字典树,采用回溯算法遍历字典树匹配所有可能出现的单词。
class Solution {
TireNode root = new TireNode();
public void insert(String word) {
TireNode temp = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (temp.getChild(c) == null) {
temp.addChild(c, new TireNode());
}
temp = temp.getChild(c);
// 标记叶子结点
if (i == word.length() - 1)
temp.setEnd();
}
}
List<String> ans = new ArrayList<>();
Set<String> set = new HashSet<>();
int[][] d = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
int n;
int m;
public List<String> findWords(char[][] board, String[] words) {
for(String word : words){
insert(word);
}
m = board.length;
n = m == 0 ? 0 : board[0].length;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
dfs(board, root.getChild(board[i][j]), i, j, new StringBuilder(), new boolean[m][n]);
}
}
for(String word : set){
ans.add(word);
}
return ans;
}
// 回溯查找单词
public void dfs(char[][] board, TireNode temp, int x, int y,StringBuilder sb, boolean[][] visited){
// 该单词不在字典树,返回
if(temp == null)
return ;
sb.append(board[x][y]);
visited[x][y] = true;
// 完整的一个单词在字典树,加入集合,并继续向下搜索
if(temp.isEnd()){
set.add(sb.toString());
}
for(int i = 0; i < 4; i++){
int tx = x + d[i][0];
int ty = y + d[i][1];
if(tx >= 0 && tx < m && ty >= 0 && ty < n && visited[tx][ty] == false){
dfs(board, temp.getChild(board[tx][ty]), tx, ty, sb, visited);
}
}
// 回溯
sb.delete(sb.length()-1, sb.length());
visited[x][y] = false;
}
}
- delete相关的pl/sql调优(r4笔记第87天)
- Java文件上传与下载【面试+工作】
- QBC查询
- 一条delete语句的调优(r4笔记第86天)
- Java支付宝接口开发【面试+工作】
- 03.SVN检出/解决冲突/提交
- Spring思维导图,让Spring不再难懂(mvc篇)
- SQL优化一(SQL使用技巧)
- Spring思维导图,让Spring不再难懂(aop篇)
- MongoDB初探第二篇 (r4笔记第82天)
- Spring思维导图,让Spring不再难懂(cache篇)
- 曲折的10g,11g中EM的安装配置过程(r4笔记第98天)
- Linux 学习记录 一(安装、基本文件操作).
- 实用的位运算应用(r4笔记第97天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法