[100天算法】-键值映射(day 42)

题目描述

实现一个 MapSum 类里的两个方法,insert 和 sum。对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。示例 1:输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/map-sum-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法 1:HashMap

复杂度分析

时间空间
insertO(1) (不考虑哈希冲突的话)O(n)
sumO(n∗len(prefix))O(n)
备注n 是 hashmap 的 key 总数,prefix 是要查找的前缀n 是字符串数量

代码

Python Code

class MapSum(object):def __init__(self):"""Initialize your data structure here."""self.hashmap = {}def insert(self, key, val):""":type key: str:type val: int:rtype: None"""self.hashmap[key] = valdef sum(self, prefix):""":type prefix: str:rtype: int"""res = 0for key in self.hashmap:if key.startswith(prefix):res += self.hashmap[key]return res# Your MapSum object will be instantiated and called as such:
# obj = MapSum()
# obj.insert(key,val)
# param_2 = obj.sum(prefix)

方法 2:Trie + DFS

思路

  • 构建前缀树。
  • sum 操作的时候,先判断 prefix 是否存在前缀树中,然后找到 prefix 最后的节点,开始 DFS。

DFS

  • 大问题dfs(node) 应该要返回以这个节点开始的所有路径中的 value 的和。

  • 小问题dfs(node.child),先找到以 node 的子节点开始的所有路径中的 value 的和。不过由于 node 有很多子节点,所以我们需要一个循环。

  • 大问题与小问题的关系:当我们求出了全部 dfs(node.child),那么

    • dfs(node) = node.value + dfs(node.child1) + dfs(node.child2) + ...
  • 递归出口:如果节点不存在,我们直接返回 0,停止递归。

复杂度分析

  • 时间复杂度:insert 操作的时间复杂度是 O(len(key)),sum 操作的时间复杂度是 O(mn),m 是字符集中字符数量,n 是字符串长度。
  • 空间复杂度:$O(m^{n})$,m 是字符集中字符数量,n 是字符串长度。
时间空间
insertO(len(key))O(mn)
sumO(mn)O(mn)
备注m 是字符集中字符数量,n 是字符串长度

代码

TypeScript Code

class TrieNode {value: number;children: Array<TrieNode>;constructor(value: number) {this.value = value;this.children = Array(26);}
}class MapSum {private root: TrieNode;constructor() {this.root = this._getTrieNode(0);}private _getTrieNode(value: number): TrieNode {return new TrieNode(value);}private _char2Index(char: string): number {return char.toLowerCase().charCodeAt(0) - 97;}insert(key: string, val: number): void {let crawl: TrieNode = this.root;for (let char of key) {const index: number = this._char2Index(char);if (!crawl.children[index]) {crawl.children[index] = this._getTrieNode(0);}crawl = crawl.children[index];}crawl.value = val;}private _dfs(crawl: TrieNode): number {if (!crawl) return 0;return crawl.children.reduce((res: number, pointer: TrieNode): number => {return res + (pointer ? this._dfs(pointer) : 0);},crawl.value,);}sum(prefix: string): number {let crawl: TrieNode = this.root;for (let char of prefix) {const index: number = this._char2Index(char);if (!crawl.children[index]) return 0;crawl = crawl.children[index];}return this._dfs(crawl);}
}/*** Your MapSum object will be instantiated and called as such:* var obj = new MapSum()* obj.insert(key,val)* var param_2 = obj.sum(prefix)*/

方法 3:Trie + 时间优化

思路

在每个节点上存 prefixSum,可以把 sum 操作的时间复杂度降到 O(1),代价是在 insert 的时候需要先检查 key 是否已经存在,存在的话要先从 prefixSum 中减去旧的 value,再加上新的 value。

复杂度分析

时间空间
insertO(len(key))O(mn)
sumO(1)O(mn)
备注m 是字符集中字符数量,n 是字符串长度

代码

class TrieNode {value: number;prefixSum: number;children: Array<TrieNode>;constructor(value: number) {this.value = value;this.prefixSum = 0;this.children = Array(26);}
}class MapSum {private root: TrieNode;constructor() {this.root = this._getTrieNode(0);}private _getTrieNode(value: number): TrieNode {return new TrieNode(value);}private _char2Index(char: string): number {return char.toLowerCase().charCodeAt(0) - 97;}search(key: string): number {let crawl: TrieNode = this.root;for (let char of key) {const index: number = this._char2Index(char);if (!crawl.children[index]) return 0;crawl = crawl.children[index];}return crawl.value;}insert(key: string, val: number): void {let crawl: TrieNode = this.root;const existedVal: number = this.search(key);for (let char of key) {const index: number = this._char2Index(char);if (!crawl.children[index]) {crawl.children[index] = this._getTrieNode(0);}crawl = crawl.children[index];crawl.prefixSum = crawl.prefixSum - existedVal + val;}crawl.value = val;}sum(prefix: string): number {let crawl: TrieNode = this.root;for (let char of prefix) {const index: number = this._char2Index(char);if (!crawl.children[index]) return 0;crawl = crawl.children[index];}return crawl.prefixSum;}
}/*** Your MapSum object will be instantiated and called as such:* var obj = new MapSum()* obj.insert(key,val)* var param_2 = obj.sum(prefix)*/

方法 4:Trie + HashMap

思路

在方法 3 的基础上再加一个 hashmap,把以某个前缀开始的 key 和相应的 value存起来,比如,

  • 我们先插入了一个 (dark, 3),然后再插入一个 (darkest, 2)
  • 这时的 hashmap 应该是这样:
    • { dark: 3, darkest: 2 }
  • 这时如果我们再次插入 (darkest, 5),覆盖了之前的值,那 hashmap 就变成了:
    • { dark: 3, darkest: 5 }

复杂度分析

时间空间
insertO(len(key))O(mn)
sumO(len(prefix))O(mn)
备注m 是字符集中字符数量,n 是字符串长度

代码

class TrieNode {value: number;prefixSum: number;children: Array<TrieNode>;constructor(value: number) {this.value = value;this.prefixSum = 0;this.children = Array(26);}
}class MapSum {private root: TrieNode;private existedWords: {[key: string]: number;};constructor() {this.root = this._getTrieNode(0);this.existedWords = {};}private _getTrieNode(value: number): TrieNode {return new TrieNode(value);}private _char2Index(char: string): number {return char.toLowerCase().charCodeAt(0) - 97;}insert(key: string, val: number): void {let crawl: TrieNode = this.root;for (let char of key) {const index: number = this._char2Index(char);if (!crawl.children[index]) {crawl.children[index] = this._getTrieNode(0);}crawl = crawl.children[index];if (key in this.existedWords) {crawl.prefixSum -= this.existedWords[key];}crawl.prefixSum += val;}this.existedWords[key] = val;crawl.value = val;}sum(prefix: string): number {let crawl: TrieNode = this.root;for (let char of prefix) {const index: number = this._char2Index(char);if (!crawl.children[index]) return 0;crawl = crawl.children[index];}return crawl.prefixSum;}
}/*** Your MapSum object will be instantiated and called as such:* var obj = new MapSum()* obj.insert(key,val)* var param_2 = obj.sum(prefix)*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119797.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.稳定性专题

1. anr https://code84.com/303466.html 一句话&#xff0c;规定的时间没有干完要干的事&#xff0c;就会发生anrsystem_anr场景 input 5sservice 前台20s 后台60scontentprivider超市 比较少见 原因 主线程耗时 复杂layout iobinder对端block子线程同步锁blockbinder被占满导…

C语言重点突破(四)自定义类型详解

前言 本文意在介绍C语言里的常规自定义类型&#xff0c;它是C语言里最重要的概念之一&#xff0c;是我们从简单使用C语言到综合运用必不可少的知识之一&#xff0c;在C语言中具有重要的地位和作用&#xff0c;掌握自定义类型的使用方法和技巧对于写出高质量的C程序是非常重要的…

prometheus监控kafka

一、前言 关于对kafka的监控&#xff0c;要求高的话可以使用kafka-exorter和jmx-exporter一起收集监控数据&#xff0c;要求不高的情况下可以使用kafka-exporter收集监控数据即可 二、部署 kafka-exporter 部署kafka-exporter&#xff0c;我是在k8s集群中部署的 编辑yaml文件…

0基础学习PyFlink——用户自定义函数之UDAF

大纲 UDAF入参并非表中一行&#xff08;Row&#xff09;的集合计算每个人考了几门课计算每门课有几个人考试计算每个人的平均分计算每课的平均分计算每个人的最高分和最低分 入参是表中一行&#xff08;Row&#xff09;的集合计算每个人的最高分、最低分以及所属的课程计算每课…

UI自动化测试工具推荐

UI自动化测试已经成为现代软件开发过程中不可或缺的一部分。它能够提供诸多优势&#xff0c;包括提高测试效率、减少人力成本、提升软件质量等。同时&#xff0c;可视化工具为UI自动化测试带来了更多便利和灵活性。然而&#xff0c;可视化工具也存在一些潜在的劣势。本文将探讨…

在k8s中 ,数据包是怎么从外部流转进入到pod的?

在 Kubernetes 中&#xff0c;当您创建 NodePort 类型的服务时&#xff0c;流量不会直接从主机的 IP 和端口转发到特定 Pod 的 IP 和端口。相反&#xff0c;流量被转发到集群中的一个节点&#xff0c;然后从那里转发到相应的 Pod。 1、当您创建 NodePort 类型的服务时&#xf…

【iOS安全】提取app对应的URLScheme

获取app的URLScheme 在已越狱的iPhone上&#xff0c;使用Filza进入app列表目录&#xff1a; /private/var/containers/Bundle/Application/ 比如我要分析Microsoft Authenticator&#xff0c;明显对应的是这里面的“Authenticator”&#xff0c;那就在Filza中点击进入“Authen…

MySQL多表关联on和where速度对比实测谁更快

MySQL多表关联on和where速度对比实测谁更快 背景 今天发现有人在讨论&#xff1a;两张MySQL的数据表按照某一个字段进行关联的时候查询&#xff0c;我们使用on和where哪种查询方式更快。百闻不如一见&#xff0c;我们来亲自测试下。 先说结论 Where、对等查询的join速度基本…

Android WMS——概述(一)

Android 中的 WMS 指的是 Window Manager Service(窗口管理服务)。WMS 是 Android 系统中的核心服务,主要分为四大部分,分别是窗口管理,窗口动画,输入系统中转站和 Surface 管理 。负责管理应用程序窗口的创建、移动、调整大小和显示等操作。 一、功能简介 WMS 的职责可…

在Eclipse中使用Junit

1、准备测试类 public class Calculator {private static int result;public void add(int n) {result result n;} public void substract(int n) {result result -1; //Bug:正确的应该是resultresult-n}public void multiply(int n) {// result result*n;方法未写好}publi…

2.9每日一题(定积分的奇偶性以及比较大小)

1、用定积分奇函数和偶函数的性质 2、用常用的基本不等式推出大小 3、用区间相同的情况下被积函数大的定积分大的定理

嵌入式系统设计师考试笔记之操作系统基础复习笔记二

目录 3、任务管理 &#xff08;1&#xff09;嵌入式操作系统的任务管理可以分为 &#xff08;2&#xff09;进程 &#xff08;3&#xff09;线程 &#xff08;4&#xff09;任务 &#xff08;5&#xff09;任务的创建与中止 &#xff08;6&#xff09;任务的状态任务有三…

Spring Boot 使用 Disruptor 做内部高性能消息队列

这里写自定义目录标题 一 、背景二 、Disruptor介绍三 、Disruptor 的核心概念3.1 Ring Buffer3.2 Sequence Disruptor3.3 Sequencer3.4 Sequence Barrier3.5 Wait Strategy3.6 Event3.7 EventProcessor3.8 EventHandler3.9 Producer 四、案例-demo五、总结 一 、背景 工作中遇…

10-动画animation

动画animation 动画-过渡和动画之间的异同-animation-name 指定要绑定到选择器的关键帧的名称&#xff0c;告诉系统需要执行哪个动画-animation-duration 动画指定需要多少秒或毫秒完成&#xff0c;告诉系统动画持续的时长-animation-timing-function 设置动画将如何完成一个周…

代码随想录算法训练营第2天| 977有序数组的平方、209长度最小的子数组。

JAVA代码编写 977. 有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&…

postgis ST_CoverageInvalidEdges使用说明

官方文档 函数说明 概要 geometry ST_CoverageInvalidEdges(geometry winset geom, float8 tolerance 0); 描述 一个窗口函数&#xff0c;用于检查窗口分区中的多边形是否形成有效的多边形覆盖范围。 它返回线性指示器&#xff0c;显示每个多边形中无效边&#xff08;如果…

使用easypoi-spring-boot-starter 4.1.1导入excel报错NoSuchMethodError和NoSuchMethodError

前言 使用easypoi进行excel的导入遇到的错误以及解决办法 easypoi项目地址&#xff1a;https://gitee.com/lemur/easypoi easypoi的Maven依赖&#xff1a; <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter<…

小程序:如何合理规划分包使主包不超过2M

背景 做过小程序项目的同学应该都有这样的经历&#xff0c;项目做着做着&#xff0c;突然发现代码包的大小超过了 2M&#xff0c;小程序无法提审&#xff0c;然后痛苦的删文件改代码来减少包大小。 虽然我们也知道小程序给我们提供了分包的功能可以减少主包的大小&#xff0c…

202305-2-矩阵运算

一、题目分析 此题本质是计算 ( W ⋅ ( Q K T ) ) V (W(QK^T))V (W⋅(QKT))V的值。 注意任务要求&#xff1a; 70% 的测试数据满足&#xff1a;n<100且d<10 。输入矩阵、向量中的元素均为整数&#xff0c;且绝对值均不超过30 。 全部的测试数据满足&#xff1a;n<1…

学习不同概率分布(二项分布、泊松分布等)概念及基础语法

概率分布是描述随机变量取值的概率情况的函数。常见的概率分布包括二项分布、泊松分布等。 二项分布&#xff08;Binomial Distribution&#xff09;&#xff1a;描述了一次试验中成功事件发生的次数的概率分布。它的基础语法如下&#xff1a; 概率质量函数&#xff1a;pmf(k, …