Leetcode 剑指 Offer II 066. 键值映射

2024-02-25 02:04
文章标签 leetcode ii offer 映射 键值 066

本文主要是介绍Leetcode 剑指 Offer II 066. 键值映射,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

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

  • MapSum() 初始化 MapSum 对象
  • void insert(String key, int val) 插入 key-val 键值对,字符串表示键 key ,整数表示值 val 。如果键 key 已经存在,那么原来的键值对将被替代成新的键值对。
  • int sum(string prefix) 返回所有以该前缀 prefix 开头的键 key 的值的总和。

示例:

  • 输入:
    • inputs = [“MapSum”, “insert”, “sum”, “insert”, “sum”]
    • inputs = [[], [“apple”, 3], [“ap”], [“app”, 2], [“ap”]]
  • 输出:
    • [null, null, 3, null, 5]
  • 解释:
    • MapSum mapSum = new MapSum();
    • mapSum.insert(“apple”, 3);
    • mapSum.sum(“ap”); // return 3 (apple = 3)
    • mapSum.insert(“app”, 2);
    • mapSum.sum(“ap”); // return 5 (apple + app = 3 + 2 = 5)

提示:

  • 1 <= key.length, prefix.length <= 50
  • key 和 prefix 仅由小写英文字母组成
  • 1 <= val <= 1000
  • 最多调用 50 次 insert 和 sum

题目思考

  1. 如何优化时间复杂度?

解决方案

思路
  • 分析题目, 一个最直接的思路就是, 使用一个字典存储所有 key-val 键值对
  • 这样 insert 时直接更新字典即可, 而针对 sum 操作, 遍历字典的所有 key, 如果它的前缀是 prefix 的话, 就累加它的值, 最终返回累加的和
  • 不过这样 sum 操作的复杂度有点高, 每次都需要遍历存储的所有 key, 有没有更高效的方法呢?
  • 在之前的题目中, 我们已经了解到, 字典树 trie 可以用来解决前缀问题 (Leetcode 剑指 Offer II 062. 实现 Trie (前缀树) / Leetcode 剑指 Offer II 063. 单词替换)
  • 这道题也不例外, 我们只需要稍作改动, 每个节点额外保存当前的值总和 sum, 同时也不需要 isWord 标记了
  • 另外我们还是需要维护 key-val 字典, 用于处理更新已有 key 的情况
  • 在执行 insert(key, val) 操作时:
    • 首先计算当前 key 对应的每个节点需要增加的值 diff
      • 如果 key 在字典中, 则 diff 就是新值减去旧值
      • 如果 key 不在字典中, 则 diff 就是 val
    • 然后维护当前节点, 初始化为根
    • 遍历单词的每个字符, 如果当前节点不存在一个对应字符的子节点时, 就创建它
    • 然后将当前节点指向对应的子节点, 同时将节点的 sum 累加之前计算好的 diff, 继续这个过程直到遍历结束
  • 在执行 sum(prefix) 操作时:
    • 维护当前节点, 初始化为根
    • 然后遍历待查询字符串的每个字符, 如果当前节点不存在一个对应字符的子节点时, 说明不存在以 prefix 为前缀的 key, 直接返回 0
    • 否则将当前节点指向对应的子节点, 继续这个过程直到遍历结束
    • 此时的当前节点的 sum 就是以该前缀 prefix 开头的键 key 的值的总和, 返回它
  • 下面代码中有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O©: 假设 C 是每个字符串的平均长度, insert 和 sum 操作都只需要对字符串的每个字符操作一次
  • 空间复杂度 O(NC): 假设 N 是 insert 操作的次数, 最差情况下, kv 字典需要存所有字符串, trie 需要存每个字符串的所有字符
代码
# trie+kv字典
class Node:def __init__(self):self.children = {}# 记录当前节点的值总和self.sum = 0class MapSum:def __init__(self):"""Initialize your data structure here."""self.root = Node()# 额外kv字典记录key:value映射self.k2v = {}def insert(self, key: str, val: int) -> None:# diff存储当前key的路径上各个节点需要增加的值# 如果当前key不在字典中, 则各个节点增加的值就是val# 否则增加的值是新值和旧值的差diff = val if key not in self.k2v else val - self.k2v[key]self.k2v[key] = valcur = self.rootfor c in key:if c not in cur.children:cur.children[c] = Node()cur = cur.children[c]# 当前节点的值总和加上diffcur.sum += diffdef sum(self, prefix: str) -> int:cur = self.rootfor c in prefix:if c not in cur.children:# 当前前缀不存在, 返回0return 0cur = cur.children[c]# 返回当前前缀的值总和return cur.sum

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

这篇关于Leetcode 剑指 Offer II 066. 键值映射的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/744101

相关文章

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

从0到1,AI我来了- (7)AI应用-ComfyUI-II(进阶)

上篇comfyUI 入门 ,了解了TA是个啥,这篇,我们通过ComfyUI 及其相关Lora 模型,生成一些更惊艳的图片。这篇主要了解这些内容:         1、哪里获取模型?         2、实践如何画一个美女?         3、附录:               1)相关SD(稳定扩散模型的组成部分)               2)模型放置目录(重要)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param