LeetCode677. Map Sum Pairs

本文介绍了一种利用前缀树(Trie)优化键值对存储和查询的方法,通过存储键值对并支持根据前缀快速计算总和,提供两种优化方案:使用前缀树存储键值对及直接存储前缀和。

Analysis

Brute-force solution

Apparently, a straightforward brute-force solution would be to store every {key, value} pair into a HashSet. And once the sum(String prefix) function is called, we scan the whole HashSet’s key set, and use String.startswith() to select keys that satisfiy our prefix requirement. Then we add up all the values and return.

Complexity

For each insert operation, the time complexity is O(1). For each sum operation, the time complexity is O(m*n) where m is the total number of keys being stored and n is the length of input prefix.

The space used by the HashMap is liner to the size of all input keys and values.

Optimization1: Use a Trie to store the {key:value} relationship

Considering that we are compare prefix, a Trie (prefix tree) is naturally appropriate for this task. So for each insert operation, we can use a Trie to store the input keys and at the end Trie node of each key, we store the value in this node.

For every sum operation, we just start from the root of the Trie and looking for the first TrieNode that has a prefix matches the given prefix. And from this node, we perform a BFS to get every key in its child branch and sum up the values.

Complexity

Each insert operation will take O(n) time where n is the length of the input key.

Each sum operation will take O(m*k) time where m is the length of the prefix and k is the number of words start with this this prefix.

The space used by the Trie is liner to the size of the total input.

Implementation

A Java implementation is showed as following:

class MapSum {
    class TrieNode {
        char curr;
        TrieNode[] child;
        boolean isEnd;
        int value;

        TrieNode(char curr) {
            this.curr = curr;
            this.child = new TrieNode[26];
        }
    }

    TrieNode root;

    /** Initialize your data structure here. */
    public MapSum() {
        // setup a dummy root node
        this.root = new TrieNode(' ');
    }

    public void insert(String key, int val) {
        char[] charKey = key.toCharArray();
        TrieNode pointer = this.root;
        for (char c : charKey) {
            if (pointer.child[c - 'a'] == null) {
                TrieNode curr = new TrieNode(c);
                pointer.child[c - 'a'] = curr;
            }
            pointer = pointer.child[c - 'a'];
        }
        pointer.isEnd = true;
        pointer.value = val;
    }

    public int sum(String prefix) {
        int ret = 0;
        if (prefix == null || prefix.isEmpty()) {
            return ret;
        }
        char[] charPrefix = prefix.toCharArray();
        TrieNode pointer = this.root;
        for (char c : charPrefix) {
            if (pointer.child[c - 'a'] == null) {
                return ret;
            }

            pointer = pointer.child[c - 'a'];
        }

        Queue<TrieNode> queue = new LinkedList<TrieNode>();
        queue.offer(pointer);
        while (!queue.isEmpty()) {
            TrieNode curr = queue.poll();
            ret += curr.value;
            for (TrieNode t : curr.child) {
                if (t == null) {
                    continue;
                }
                queue.offer(t);
            }
        }

        return ret;
    }
}

Optimization2: Store the sum directly

Actually there is still some unnecessary work we have done in the above approach. Since we only cares about the prefix sum rather than the exact value that is related to this key, we do not need to store the values corresponding to each input key. Instead, we can store the sum of current prefix in the Trie. In this way, the insert is still the same with the above process, but during the insert process, rather than only store the value at the end TrieNode of current key, we store the sum of every prefix along the way.

Complexity

The time complexity of insert operation is O(n) where n is the length of the input key.

The time complexity of sum operation is O(m) where m is the length of the input prefix.

The space used is still linear to the total input size.

Implementation

A Java implementation is showed as following:

class MapSum {
    class TrieNode {
        TrieNode[] child = new TrieNode[26];
        int val;
    }

    HashMap<String, Integer> map;
    TrieNode root;

    /** Initialize your data structure here. */
    public MapSum() {
        this.map = new HashMap<>();
        this.root = new TrieNode();
    }

    public void insert(String key, int val) {
        int delta = val - map.getOrDefault(key, 0);
        map.put(key, val);
        TrieNode curr = this.root;
        curr.val += delta;
        for (char c : key.toCharArray()) {
            if (curr.child[c - 'a'] == null) {
                curr.child[c - 'a'] = new TrieNode();
                curr.child[c - 'a'].val = delta;
            } else {
                curr.child[c - 'a'].val += delta;
            }

            curr = curr.child[c - 'a'];
        }
    }

    public int sum(String prefix) {
        if (prefix == null || prefix.isEmpty()) {
            return 0;
        }
        TrieNode curr = this.root;
        for (char c : prefix.toCharArray()) {
            if (curr.child[c - 'a'] == null) {
                return 0;
            }
            curr = curr.child[c - 'a'];
        }

        return curr.val;
    }
}
### LeetCode Top 100 Problems 的 JavaScript 解决方案 LeetCode 是一个汇聚了大量高质量算法题目的平台,对于希望提高编程能力尤其是算法和数据结构理解的人来说非常有价值。针对寻找 LeetCode 上用 JavaScript 编写的前 100 道热门练习题的需求,可以参考多个开源项目中的资源。 #### 开源项目的贡献者们已经整理并实现了大量的 LeetCode 题目解答,在这些项目中能够找到许多经典题目及其对应的 JavaScript 实现方法[^1]: - **leetcode-js**: 这个项目包含了超过两千道 LeetCode 问题的 JavaScript 版本解答,几乎覆盖了所有的公开题目,自然也包括了所谓的 "Top 100" 或其他形式的经典或高频面试题目列表。 为了更具体地展示如何获取这 100 道题目的 JavaScript 解法,可以从 `leetcode-js` 中挑选一些常见的例子作为示范。以下是几个典型问题以及它们的简化版 JavaScript 实现方式: #### 示例一:两数之和 (Two Sum) 这个问题要求在一个整数数组中找出两个不同的索引位置使得这两个位置处的数值相加等于给定的目标值。 ```javascript function twoSum(nums, target) { const map = new Map(); for (let i = 0; i < nums.length; ++i) { let complement = target - nums[i]; if (map.has(complement)) { return [map.get(complement), i]; } map.set(nums[i], i); } } ``` #### 示例二:有效的括号 (Valid Parentheses) 此题的任务是验证输入字符串所表示的一组圆括号、方括号和花括号是否有效闭合。 ```javascript function isValid(s) { const stack = []; const pairs = { ')': '(', '}': '{', ']': '[' }; for (const char of s) { if ('({['.includes(char)) stack.push(char); else if (!stack.length || stack.pop() !== pairs[char]) return false; } return !stack.length; } ``` 上述代码片段仅展示了部分 LeetCode “Top 100” 列表内的题目实例;完整的解决方案可以在提到的相关 GitHub/GitCode 库里找到更多细节和其他类型的算法挑战[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

耀凯考前突击大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值