知识点回顾 : 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专栏,每日更新!




















