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

news/2024/7/20 20:41:50 标签: 算法, 深度优先

题目描述

实现一个 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] = val

    def sum(self, prefix):
        """
        :type prefix: str
        :rtype: int
        """
        res = 0
        for 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.niftyadmin.cn/n/5129287.html

相关文章

Builder 请进:波卡最新开发入门指南

撰文&#xff1a;Dennis Zoma 编译&#xff1a;OneBlock 社区 本文更新于 2023 年 10 月 3 日&#xff0c;来源&#xff1a;https://wiki.polkadot.network/docs/build-guide Polkadot 是一个区块链协议&#xff0c;有两个目标&#xff1a;在所有连接的平行链之间提供共享安全…

如何优化工业5G网关的网络信号

工业5G网关&#xff0c;通常是指支持5G网络&#xff0c;具有高速率、低时延、广接入等特点的高性能工业物联网智能网关&#xff0c;这类网关具有强大的设备接入能力、通信协议转换、运算处理能力、联动控制能力&#xff0c;有助于提升工业物联网整体通信效率&#xff0c;实现生…

Linux中shell的执行流控制

目录 一、for语句 1、for语句的基本格式 2、示例 二、条件语句 1、while…do语句 2、until…do语句 3、if …then语句 4、示例 三、case语句 四、expect应答语句 1、固定答案 2、将expect与bash环境结合 3、示例 五、终止语句 一、for语句 作用&#xff1a;为…

单位建数字档案室的意义和作用

单位建立数字档案室的意义和作用包括&#xff1a; 1.提高档案管理效率。数字档案室可以高效地收集、整理和存储电子文档&#xff0c;通过数字化处理&#xff0c;文档的查找和检索速度大幅提升。 2.降低管理成本。数字档案室可以通过节约空间和人力成本&#xff0c;降低管理成本…

增强常见问题解答搜索引擎:在 Elasticsearch 中利用 KNN 的力量

在快速准确的信息检索至关重要的时代&#xff0c;开发强大的搜索引擎至关重要。 随着大型语言模型和信息检索架构&#xff08;如 RAG&#xff09;的出现&#xff0c;在现代软件系统中利用文本表示&#xff08;向量/嵌入&#xff09;和向量数据库已变得越来越流行。 在本文中&am…

计算机网络【CN】子网划分与子网掩码

一个子网定义(X.X.X.X/n) 子网掩码为 n 个 1&#xff0c;32-n 个 0包含的 IP 地址数&#xff1a;232−n 主机号全 0 表示本网段主机号全 1 表示网段的广播地址可分配的 IP 地址数 :232−&#x1d45b;−2 子网划分原则 满足子网定义子网&#x1d434;1…&#x1d434;&#x…

大厂面试题-Java常见的垃圾收集器有哪些

概述 实际上&#xff0c;垃圾收集器(GC&#xff0c;Garbage Collector)是和具体JVM实现紧密相关的&#xff0c;不同厂商(IBM、Oracle)&#xff0c;不同版本的JVM&#xff0c;提供的选择也不同。接下来&#xff0c;我来谈谈最主流的Oracle JDK。 Serial GC&#xff0c;它是最古…

EtherCAT主站SOEM-- 0 SOEM下载编译及文件功能介绍

0 介绍EtherCAT主站SOEM文件及主要功能函数 1. soem介绍&#xff1a;2 soem主要功能文件说明&#xff1a;3 soem下载链接4 编译soem4.1 Windows (Visual Studio)&#xff1a;4.2 Linux & macOS&#xff1a; 该文档修改记录&#xff1a;总结 1. soem介绍&#xff1a; SOEM&…