概述:
Trie是个简单但实用的数据结构,是一种树形结构,是一种哈希树的变种,相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。
例如:pool,prize,preview,prepare,produce,progress这些关键词的Tire树
典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
- 每个节点的所有子节点包含的字符都不相同。
应用场景:
字典数查找效率很高,时间复杂度是O(m),m是要查找的单词中包含的字母的个数,但是会浪费大量存放空指针的存储空间,属于以空间换时间的算法。
1、串快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
2、单词自动完成
编辑代码时,输入字符,自动提示可能的关键字、变量或函数等信息。
3、最长公共前缀
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为最近公共祖先问题。
4、串排序方面的应用
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。
算法题目
在力扣上也有关于Tire树的题目
208. 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
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
说明:
你可以假设所有的输入都是由小写字母 a-z 构成的。
保证所有输入均为非空字符串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
实现一:
class Trie {
public:
/** Initialize your data structure here. */
Trie() {
}
/** Inserts a word into the trie. */
void insert(string word) {
Trie* node = this;
for(auto ch : word)
{
if(!node->next.count(ch))
node->next[ch] = new Trie();
node = node->next[ch];
}
node->is_word = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Trie* node = this;
for(auto &ch : word)
{
if(!node->next.count(ch))
return false;
node = node->next[ch];
}
return node->is_word;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie* node = this;
for(auto &ch : prefix)
{
if(!node->next.count(ch))
return false;
node = node->next[ch];
}
return true;
}
private:
bool is_word = false;
map<char, Trie*> next = {};
};
/**
* 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);
*/
实现二:该实现方法只是单纯为了解题,并不是实现出Tire这个数据结构
class Trie {
public:
struct tireNode {
char c;
map<char, tireNode*>next;
bool endword;
tireNode(char chr, bool happyend) : c(chr), endword(happyend){}
};
map<char, tireNode*>head;
/** Initialize your data structure here. */
Trie() {
}
/** Inserts a word into the trie. */
void insert(string word) {
if(word.size() == 0)
return;
char headchar = word[0];
tireNode* tmphead = NULL;
if(head.find(headchar) == head.end())
{
head[headchar] = new tireNode(headchar, false);
}
tmphead = head[headchar];
for(int i = 1; i < word.size(); i++)
{
if(tmphead->next.find(word[i]) == tmphead->next.end())
{
tmphead->next[word[i]] = new tireNode(word[i], false);
}
tmphead = tmphead->next[word[i]];
}
tmphead->endword = true;
return;
}
/** Returns if the word is in the trie. */
bool search(string word) {
if(word.size() == 0)
return true;
char headchar = word[0];
if(head.find(headchar) == head.end())
return false;
tireNode *tmphead = head[headchar];
for(int i = 1; i < word.size(); i++)
{
if(tmphead->next.find(word[i]) == tmphead->next.end())
return false;
tmphead = tmphead->next[word[i]];
}
if(tmphead->endword)
return true;
else
return false;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
if(prefix.size() == 0)
return true;
char headchar = prefix[0];
if(head.find(headchar) == head.end())
return false;
tireNode *tmphead = head[headchar];
for(int i = 1; i < prefix.size(); i++)
{
if(tmphead->next.find(prefix[i]) == tmphead->next.end())
return false;
tmphead = tmphead->next[prefix[i]];
}
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);
*/