知识点回顾 : Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。
❓677. 键值映射
难度:中等
设计一个 map
,满足以下几点:
- 字符串表示键,整数表示值
- 返回具有前缀等于给定字符串的键的值的总和
实现一个 MapSum
类:
MapSum()
初始化MapSum
对象void insert(String key, int val)
插入key-val
键值对,字符串表示键key
,整数表示值val
。如果键key
已经存在,那么原来的键值对key-value
将被替代成新的键值对。int sum(string prefix)
返回所有以该前缀prefix
开头的键key
的值的总和。
示例:
输入:
[“MapSum”, “insert”, “sum”, “insert”, “sum”]
[ [], [“apple”, 3], [“ap”], [“app”, 2], [“ap”]]
输出:
[null, null, 3, null, 5]
解释:
MapSum mapSum = new MapSum();
mapSum.insert(“apple”, 3);
mapSum.sum(“ap”); // 返回 3 (apple = 3)
mapSum.insert(“app”, 2);
mapSum.sum(“ap”); // 返回 5 (apple + app = 3 + 2 = 5)
提示:
1 <= key.length, prefix.length <= 50
key
和prefix
仅由小写英文字母组成1 <= val <= 1000
- 最多调用
50
次insert
和sum
💡思路:
我们要定义一个名为TrieNode
的类,它有两个属性:
val
:表示所有到达该节点的前缀prefix
开头的键key
的值的总和;children
:这是一个大小为26的数组,表示当前节点的子节点。数组的每个元素代表一个字母(从a
到z
)。如果当前节点有一个子节点(例如a
),则children
数组的相应位置(即索引0)将包含一个TrieNode
对象。insert
操作:我们首先求出前缀对应的值的改变num
,我们直接在TrieNode
节点上更新键key
的每个前缀对应的值。sum
操作: 我们直接在前缀树上搜索该给定的前缀对应的值即可,如果给定的前缀不在前缀树中,则返回0
。
🍁代码:(Java、C++)
Java
class TrieNode{
int val;
TrieNode[] children = new TrieNode[26];
TrieNode(){
this.val = 0;
for(int i = 0; i < 26; i++){
this.children[i] = null;
}
}
}
class MapSum {
private TrieNode root;
private Map<String, Integer> cnt;
public MapSum() {
this.root = new TrieNode();
cnt = new HashMap();
}
public void insert(String key, int val) {
int num = val - cnt.getOrDefault(key, 0);
cnt.put(key, val);
TrieNode node = root;
for(char c : key.toCharArray()){
if(node.children[c - 'a'] == null){
node.children[c - 'a'] = new TrieNode();
}
node = node.children[c - 'a'];
node.val += num;
}
}
public int sum(String prefix) {
TrieNode node = root;
for(char c : prefix.toCharArray()){
if(node.children[c - 'a'] == null){
return 0;
}
node = node.children[c - 'a'];
}
return node.val;
}
}
/**
* Your MapSum object will be instantiated and called as such:
* MapSum obj = new MapSum();
* obj.insert(key,val);
* int param_2 = obj.sum(prefix);
*/
C++
struct TrieNode{
int val;
TrieNode* children[26];
TrieNode(){
this->val = 0;
for(int i = 0; i < 26; i++){
this->children[i] = nullptr;
}
}
};
class MapSum {
private:
TrieNode* root;
unordered_map<string, int> cnt;
public:
MapSum(){
this->root = new TrieNode();
}
void insert(string key, int val) {
int num = val;
if(cnt.count(key)) {
num -= cnt[key];
}
cnt[key] = val;
TrieNode* node = root;
for(auto c : key){
if(node->children[c - 'a'] == nullptr){
node->children[c - 'a'] = new TrieNode();
}
node = node->children[c - 'a'];
node->val += num;
}
}
int sum(string prefix) {
TrieNode* node = root;
for(auto c : prefix){
if(node->children[c - 'a'] == nullptr){
return 0;
}else{
node = node->children[c - 'a'];
}
}
return node->val;
}
};
/**
* Your MapSum object will be instantiated and called as such:
* MapSum* obj = new MapSum();
* obj->insert(key,val);
* int param_2 = obj->sum(prefix);
*/
🚀 运行结果:
🕔 复杂度分析:
-
时间复杂度:
insert
操作时间复杂度为 O ( n ) O(n) O(n),其中n
是插入的字符串key
的长度。sum
操作时间复杂度为 O ( n ) O(n) O(n),其中 O ( n ) O(n) O(n) 为给定的查询字符的长度,需要在前缀树中搜索给定的前缀。 -
空间复杂度: O ( c n m ) O(cnm) O(cnm),其中
m
表示key-val
键值的数目,n
表示字符串key
的最大长度,c
为常数。
题目来源:力扣。
放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我 leetCode专栏,每日更新!