前缀树Trie
什么是前缀树
前缀树(Trie),也称为字典树或单词查找树,是一种用于高效存储和检索字符串集合的数据结构,特别是用于字符串集合的前缀查询操作。前缀树非常适合处理字符串的前缀匹配问题,如自动补全、拼写检查和单词搜索等。
前缀树(Trie)的基本特点
- 节点表示字符:
- 每个节点表示一个字符,根节点通常不代表任何字符(可以认为是一个空字符)。
- 从根节点到某一节点的路径上的字符序列表示一个字符串的前缀。
- 边连接字符:
- 从一个节点到它的子节点的边表示该节点字符后接的下一个字符。
- 边上的字符决定了从当前节点到子节点的路径。
- 存储终止标记:
- 节点通常包含一个布尔值(或其他标记)来表示从根节点到该节点的路径是否构成一个有效的单词(终止标记)。
前缀树的主要操作
- 插入字符串:
- 从根节点开始,根据字符串中的每个字符,沿着现有的路径或创建新的节点,直到字符串的最后一个字符。
- 在最后一个字符的节点处标记该路径为一个完整的单词。
- 查找字符串:
- 从根节点开始,按照字符串中的每个字符遍历路径。
- 如果能找到完整的路径且路径上的节点都存在,那么该字符串存在于前缀树中。
- 前缀匹配:
- 与查找字符串类似,但只需要找到前缀路径,不需要是完整的单词路径。
- 可以用来实现自动补全或查找所有以某一前缀开头的单词。
前缀树的示例
假设我们要构建一个包含单词 “cat”, “car”, “dog”, “dot” 的前缀树:
(root)
/ \
c d
/ \ / \
a a o o
/ \ \
t r g
\
t
前缀树的优点
- 高效的前缀查询:能够快速找到具有相同前缀的所有单词。
- 节省空间:共享前缀的单词只存储一次,共用节点。
代码实现
#include <vector>
#include <string>
using namespace std;
class Trie {
private:
vector<Trie*> children; // 子节点数组
bool isEnd; // 标记当前节点是否为单词的结束节点
// 在 Trie 树中搜索以 prefix 为前缀的节点
Trie* searchPrefix(string prefix)
{
Trie* node = this; // 从根节点开始搜索
for (char ch : prefix) // 遍历前缀字符串的每个字符
{
ch -= 'a'; // 将字符转换为索引值
if (node->children[ch] == nullptr) // 如果当前字符的子节点为空,说明前缀不存在
{
return nullptr;
}
node = node->children[ch]; // 继续向下搜索
}
return node; // 返回以 prefix 为前缀的节点
}
public:
// 构造函数,初始化 Trie 树的根节点和结束标志
Trie() : children(26), isEnd(false) {}
// 向 Trie 树中插入一个单词
void insert(string word)
{
Trie* node = this; // 从根节点开始插入
for (char ch : word) // 遍历单词的每个字符
{
ch -= 'a'; // 将字符转换为索引值
if (node->children[ch] == nullptr) 如果当前字符的子节点为空,创建新节点
{
node->children[ch] = new Trie();
}
node = node->children[ch]; // 继续向下插入
}
node->isEnd = true; // 设置当前节点为单词的结束节点
}
// 搜索 Trie 树中是否存在单词 word
bool search(string word)
{
Trie* node = this->searchPrefix(word); // 先搜索以 word 为前缀的节点
return node != nullptr && node->isEnd; // 如果节点存在且是单词的结束节点,则返回 true
}
// 判断 Trie 树中是否存在以 prefix 为前缀的单词
bool startsWith(string prefix)
{
return this->searchPrefix(prefix) != nullptr; // 直接调用 searchPrefix 函数判断前缀是否存在
}
};
/**
* 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);
*/
前缀树可以解决什么问题
前缀树(Trie)是一种非常有用的数据结构,可以解决以下几类问题:
- 字符串的快速搜索:前缀树能够高效地存储大量字符串,并且可以快速查找特定的字符串是否存在于集合中。这使得前缀树在搜索引擎、拼写检查器和自动补全等应用中非常有用。
- 自动补全和预测输入:利用前缀树可以实现快速的自动补全功能。例如,当用户输入一个前缀时,前缀树可以快速找到所有以该前缀开头的单词,从而为用户提供可能的选项。
- 拼写检查:前缀树可以用于检查单词的拼写是否正确。通过在前缀树中搜索输入单词的所有可能前缀,可以判断输入单词是否存在于字典中。
- 单词频率统计:前缀树可以用于统计文本中单词的出现频率。通过在前缀树中插入单词,并在每个单词节点上维护一个计数器,可以快速地计算出每个单词出现的次数。
- 前缀匹配:前缀树可以用于快速查找具有特定前缀的所有字符串。这对于查找电话号码、IP 地址或文件路径等具有特定前缀的条目非常有用。