算法简介
Trie树
,即字典树
,也有的称为前缀树
,是一种树形结构。广泛应用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是最大限度地减少无谓的字符串比较,查询效率比较高。Trie的核心思想是空间换时间,利用字符串的
公共前缀
来降低查询时间的开销以达到提高效率的目的。
算法特征
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
- 它的key都为字符串,能做到高效查询和插入,时间复杂度为O(k),k为字符串长度,缺点是如果大量字符串没有共同前缀时很耗内存。
适用范围
- 单词检索
- 统计和排序字符串(但不仅限于字符串)
- 字符串前缀
算法步骤
- 定义字典树节点
- 构造字典树(也就是insert操作)
- 根据题目要求搜索字典树,在搜索的过程中进行结果的处理
LeetCode: 677.键值映射.
实现一个 MapSum 类,支持两个方法,insert 和 sum:
MapSum() 初始化 MapSum 对象 void insert(String key, int val) 插入 key-val
键值对,字符串表示键 key , 整数表示值 val 。如果键 key 已经存在,那么原来的键值对将被替代成新的键值对。
int sum(string prefix) 返回所有以该前缀 prefix 开头的键 key 的值的总和。
// 字典树节点定义
struct TrieNode {
int val; // 对应的和值
TrieNode * next[26]; // 26个字母使用数字替换 --> 指向下一个节点
TrieNode() {
this->val = 0;
for (int i = 0; i < 26; ++i) {
// 默认赋值为null
this->next[i] = nullptr;
}
}
};
class MapSum {
public:
MapSum() {
// 创建根节点,根节点不含值
this->root = new TrieNode();
}
void insert(string key, int val) {
int delta = val;
if (cnt.count(key)) {
// 如果已经存在该路径,则会被覆盖,则要加上的值为: 新值 - 旧值
delta -= cnt[key];
}
cnt[key] = val;
TrieNode * node = root;
for (auto c : key) {
// 如果不存在,则创建节点,存在则接着往下寻找
if (node->next[c - 'a'] == nullptr) {
node->next[c - 'a'] = new TrieNode();
}
// 节点加上值
node = node->next[c - 'a'];
node->val += delta;
}
}
int sum(string prefix) {
TrieNode * node = root;
for (auto c : prefix) {
// 找不到返回 0
if (node->next[c - 'a'] == nullptr) {
return 0;
} else {
node = node->next[c - 'a'];
}
}
// 找到了,返回对应节点的值
return node->val;
}
private:
TrieNode * root;
unordered_map<string, int> cnt;
};