返回

算法进阶:Trie 模板的应用——LeetCode 677. 键值映射

后端

LeetCode 677. 键值映射

题目

实现一个 MapSum 类,支持两个方法,insert 和 sum:

  • insert(string key, int val):插入一个键值对 key、val。如果键 key 已经存在,那么原来的键值对将被覆盖。
  • sum(string prefix):返回所有以 prefix 为前缀的键 key 的值的总和。

示例:

MapSum mapSum = new MapSum();
mapSum.insert("apple", 3);
int total = mapSum.sum("ap");           // return 3 (apple = 3)
mapSum.insert("app", 2);
total = mapSum.sum("ap");           // return 5 (apple + app = 3 + 2)

问题分析

本题需要解决的问题是如何存储和查询键值对,以及如何计算所有以某个前缀开头的键的值的总和。

解决方案

我们将使用 Trie 模板来存储键值对,并使用 DFS 算法来计算所有以某个前缀开头的键的值的总和。

1. Trie 模板

Trie,又称字典树,是一种树形数据结构,用于存储字符串。它具有以下特点:

  • 每个结点代表一个字符。
  • 根结点不包含字符。
  • 从根结点到每个叶结点的路径表示一个字符串。
  • 叶结点表示一个完整的字符串。

2. DFS 算法

DFS,又称深度优先搜索,是一种遍历树形数据结构的算法。它的基本思想是:

  • 从根结点开始,依次访问其所有子结点。
  • 当访问到叶结点时,对该叶结点进行处理。
  • 然后回溯到该叶结点的父结点,继续访问其剩余的子结点。

3. 代码实现

class MapSum:

    def __init__(self):
        self.root = {}
        self.score = {}

    def insert(self, key: str, val: int) -> None:
        node = self.root
        for char in key:
            if char not in node:
                node[char] = {}
            node = node[char]
        node['#'] = val  # 表示 key 已经插入完成,可以计算 score 了
        self.score[key] = val  # 将 key-val 存储在 score 中,便于快速查询

    def sum(self, prefix: str) -> int:
        node = self.root
        total = 0
        for char in prefix:
            if char not in node:
                return 0  # prefix 不存在,返回 0
            node = node[char]

        # 找到前缀 prefix 的最后一个结点
        # 再使用 DFS 遍历所有以该结点为起点的子结点,计算 score
        def dfs(node, cur):
            nonlocal total
            for char in node:
                if char == '#':  # 表示遇到一个 key
                    total += cur
                else:
                    dfs(node[char], cur + char)  # 继续往下遍历

        dfs(node, prefix)
        return total

总结

本题通过将 Trie 模板与 DFS 算法相结合,实现了一个 MapSum 类,支持两个方法,insert 和 sum。insert 方法用于插入键值对,sum 方法用于计算所有以某个前缀开头的键的值的总和。