文章目录
前言
字典树作为一种十分重要的数据结构,在大学的数据结构课堂上,抑或是算法课堂上都没有介绍。而同时又有许多小伙伴想要快速简单的了解字典树。那么这一系列文章将会通过文字+实战的方式教会你如何去学习字典树,话不多说,我们进入正题。
`
一、什么是字典树
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。[摘录自百度百科]
简而言之,字典树的功能可以让我们以下几点:
(1) 判断一个字符串是否在已有的字符串中出现
(2) 判断一个字符串是否是已有的字符串的前缀
二、图形化字典树
在这个字典树中记录了三个字符串,分别是aac 、 b 、 za。我们所需要做的就是构建出这么一棵字典树,然后做一些例如查询a是否是其中一个字符串的前缀,是否存在字符串za之类的工作即可。
二、字典树代码模板
1、分块介绍字典树模板
(该模板只包含字符串中只有26个字母,其他情况另当别论,不过学会这个可以举一反三,触类旁通)
1.1 定义字典序中节点
class TrieNode{//定义结点
public:
bool end;
TrieNode *next[26];
TrieNode(){
end = false;
memset(next, 0, sizeof(next));
}
};
数据结构定义部分,end是用来判断是否存在该字符串。TrieNode *next[26] 表示26个字母,0表示a,25表示z。
1.2 初始化字典序
TrieNode *root;
Trie() {
root = new TrieNode();
}
字典树创建一个头节点,设置为空状态。
1.3 在字典树中插入字符串
void insert(string word) {//用来在字典树中插入一个单词
TrieNode *now = root;
for(int i = 0; i < word.size(); ++i){
int child = word[i] - 'a';
if(now->next[child] == nullptr){
now->next[child] = new TrieNode();
}
now = now->next[child];
}
now->end = true;
}
首先创建一个指针now指向头节点,然后开始从word的第一个字母开始到最后一个字母建立字典树中的节点。因为我们建立的字典树是26叉树,我们判断next[0] 表示指向字母为a的节点。首先先用一个int型整数child将字符转化为对应的数字,然后判断该26叉树中是否存在以child为节点的子树,没有的化则建立子树,并将now移动到相应的下一个位置。最终将now指针指向的end设置为true。
1.4 在字典树中查询字符串
bool search(string word) {//查找一个单词是否在字典树中已经出现
TrieNode *now = root;
for(int i = 0; i < word.size(); ++i){
int child = word[i] - 'a';
if(now->next[child] == nullptr){
return false;
}
now = now->next[child];
}
return now->end;
}
按照建树的遍历思路来进行查询,一直对字符串从开始遍历到末尾,如果字母对应的字符在字典树中不存在(为空)则查询不到,查询到就移动到下一个节点。
最后判断now指针指向的end是否为true,如果为true则代表存在该字符串,否则不存在该字符串。
1.5 在字典树中判断字符串是否是已出现字符串的前缀
bool startsWith(string prefix) {//用来判断该前缀是否是字典树中一个单词的前缀
TrieNode *now = root;
for(int i = 0; i < prefix.size(); ++i){
int child = prefix[i] - 'a';
if(now->next[child] == nullptr){
return false;
}
now = now->next[child];
}
return true;
}
直接按照上述建树或者查询字符串的思路来进行遍历,如果遍历到空节点则不存在该前缀,如果遍历完毕,则返回true(存在以该字符串为前缀的字符串)。
2、实战演练
1、 题号
208. 实现 Trie (前缀树)
2、题目链接
点击跳转到题目位置
3、题目描述
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false
4、题目代码 (如果认真学习了上面的代码模块,是不是十分熟悉呢~)
class TrieNode{//定义结点
public:
bool end;
TrieNode *next[26];
TrieNode(){
end = false;
memset(next, 0, sizeof(next));
}
};
class Trie {
public:
TrieNode *root;
Trie() {
root = new TrieNode();
}
void insert(string word) {//用来在字典树中插入一个单词
TrieNode *now = root;
for(int i = 0; i < word.size(); ++i){
int child = word[i] - 'a';
if(now->next[child] == nullptr){
now->next[child] = new TrieNode();
}
now = now->next[child];
}
now->end = true;
}
bool search(string word) {//查找一个单词是否在字典树中已经出现
TrieNode *now = root;
for(int i = 0; i < word.size(); ++i){
int child = word[i] - 'a';
if(now->next[child] == nullptr){
return false;
}
now = now->next[child];
}
return now->end;
}
bool startsWith(string prefix) {//用来判断该前缀是否是字典树中一个单词的前缀
TrieNode *now = root;
for(int i = 0; i < prefix.size(); ++i){
int child = prefix[i] - 'a';
if(now->next[child] == nullptr){
return false;
}
now = now->next[child];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
5、题解
如果无法AC这道题目,可以重新去看字典树的模板,多思考一下,相信你一定会做出来的!
总结
以上就是字典树的基础内容了,走入字典树系列收录于算法与数据结构专栏,旨在让希望理解字典树的能迅速入门字典树,并且通过实战能熟练掌握字典树的一些基本用法与变式。本篇文章是基础中的基础,一定要掌握,后面几篇将会从实战来巩固相应知识。