java数据结构与算法刷题-----LeetCode677. 键值映射

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846

在这里插入图片描述

1. hash表+暴力扫描

解题思路:时间复杂度insert O(1),sum O(n * m),其中n是key的数量,m是前缀长度。空间复杂度O(n * m)
  1. 直接用一个map存储key和val
  2. sum时,直接遍历map中所有的key值,只要和要找的prefix前缀具有包含关系,就累加
  3. 所以这个办法,就是纯粹的暴力求解,每sum一个前缀,都需要比对每一个key的每一个字母
代码

在这里插入图片描述

class MapSum {
    Map<String, Integer> map;//map

    public MapSum() {
        map = new HashMap<>();
    }
    //插入前缀时,直接插入
    public void insert(String key, int val) {
        map.put(key,val);
    }
    //求和时
    public int sum(String prefix) {
        int res = 0;
        for (String s : map.keySet()) {//每个键都遍历一遍
            if (s.startsWith(prefix)) {//只要当前键的前缀包含prefix
                res += map.get(s);//结果就累加
            }
        }
        return res;
    }
}

2. 前缀和映射

和上面不一样的是,这个思路,插入的时间开销多,但是查的时候快。上面的思路是插入快,但是查的慢。

解题思路:时间复杂度insert O( n 2 n^2 n2),sum O(1),其中n是key的长度。空间复杂度O(n * m)
  1. 将每个要插入的key,都将所有可能的前缀进行拆分,例如apple拆为a,ap,app,appl,apple.
  2. 那么插入这些子前缀时,一定是以累加的形式,就是先获取原来的值,然后将这个累加插入
  3. 但是此时就有一个问题,如果我们再次插入apple,并且对应val不一样呢?
  4. 假设这次apple对应的val我们想要改为比上次少5.那么a,ap,app,appl,apple.就都得-5
  5. 怎么做才能方便的进行这个都-5的操作呢?
  1. 引入一个delta变量
  2. 如果key不存在,delta等于val
  3. 如果key存在,获取已存在key的值,让delta = val - map[key]. 那么就获取这次的val与上次插入的差值了
  4. 然后对a,ap,app,appl,apple.累加delta即可
代码

在这里插入图片描述

class MapSum {
    Map<String, Integer> map;//map
    Map<String, Integer> prefixmap;//保存前缀

    public MapSum() {
        map = new HashMap<>();
        prefixmap = new HashMap<>();
    }
    //插入时
    public void insert(String key, int val) {
        //若map中存在这个key,获取当前要插入的key的val和其的差值
        //因为重复插入key时,这个key我们每个前缀都映射到了prefixmap
        //不能只改map中key的映射,还要将所有其它映射都改了
        //那么获取差值,在重新累加一遍即可
        int delta = val - map.getOrDefault(key, 0);
        map.put(key, val);//将键值对放入map
        for (int i = 1; i <= key.length(); ++i) {//遍历这个前缀
            String currprefix = key.substring(0, i);//获取这个前缀的所有顺序组合,例如a,ap,app,appl,apple
            prefixmap.put(currprefix, prefixmap.getOrDefault(currprefix, 0) + delta);//将顺序组合放入
        }
    }
    //获取时,直接获取即可。
    public int sum(String prefix) {
        return prefixmap.getOrDefault(prefix, 0);
    }
}

3. 前缀树(字典树)Trie树

前缀树是专门处理类似这种情况的数据结构

🏆LeetCode208. 实现 Trie (前缀树)https://blog.csdn.net/grd_java/article/details/136530650
解题思路:时间复杂度insert O(n),sum O(n),其中n是key的长度。空间复杂度O(c * n * m),其中m是键值对数量,c是常数
  1. 利用前缀树的特性,插入时,一层一层按照字符插入即可,查找时也是一样
  2. 因为这道题依然是所有字符串都是英文字母,所以还是一棵26叉树,代表26个字母
  3. 通过26个不同的字母,按照不同的顺序和长度组合,理论上可以组合出世界上所有的单词
  4. 但是这道题,还指定一个val值,这个是前缀树自己做不到的

所以借用方法二的思路,用一个map存储插入的key和val,引入delta变量

  1. 如果key不存在,delta等于val
  2. 如果key存在,获取已存在key的值,让delta = val - map[key]. 那么就获取这次的val与上次插入的差值了.
  3. 这样我们就可以处理前缀树中每个前缀的和。因为前缀树每个结点保存都是val累加完成之后的结果,现在的情况是,某一次的累加数组val产生了变化,我们不知道是和上次差多少,所以只能用一个map来记录,让我们知道和上次差多少,即时修改val
代码

在这里插入图片描述

class MapSum {
    class TrieNode {//前缀树结点
        int val = 0;//val值
        TrieNode[] next = new TrieNode[26];//每个结点都有26种可能,代表26个英文字母
    }

    TrieNode root;
    Map<String, Integer> map;//为了避免重复插入相同key,但是不同val的情况,我们需要一个map来记录

    public MapSum() {//初始化前缀树
        root = new TrieNode();
        map = new HashMap<>();
    }
    
    public void insert(String key, int val) {        
        int delta = val - map.getOrDefault(key, 0);//同样获取与上次相同key插入时val的差值
        map.put(key, val);//将新的val入map
        TrieNode node = root;//遍历前缀树
        for (char c : key.toCharArray()) {//用key的字符,一个个插入
            if (node.next[c - 'a'] == null) {//如果当前前缀组合还没有插入过
                node.next[c - 'a'] = new TrieNode();//初始化,并插入
            }
            node = node.next[c - 'a'];//处理下一个结点
            node.val += delta;//结点val累加
        }
    }
    //求和操作
    public int sum(String prefix) {
        TrieNode node = root;//遍历前缀树
        for (char c : prefix.toCharArray()) {//用要找的前缀prefix,一个字符一个字符找
            if (node.next[c - 'a'] == null) {//如果当前字符比对不上
                return 0;//返回0
            }
            node = node.next[c - 'a'];//否则表示比对成功,进行下一个字符的比对
        }
        return node.val;//比对完成后,返回val值
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

殷丿grd_志鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值