记住这个模板,闭着眼都能写出【前缀树】

391 阅读1分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

题目

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

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

示例

输入:
["MapSum", "insert", "sum", "insert", "sum"]
[[], ["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
  • keyprefix 仅由小写英文字母组成
  • 1 <= val <= 1000
  • 最多调用 50insertsum

解题思路

前缀树

对于insert方法,很多方式都能实现,但是后面的sum方法,要求的是找到以前缀prefix开头的所有键的值总和,这时候就得采用前缀树来实现来,采用前缀树我们可以快速找到需要的前缀及其后续所有节点,再通过深度搜索来汇总所有的值并返回即可。

前缀树模版

class PrefixTree{
    // 定义一个数组,每个字符对应一个位置
    private PrefixTree[] next;
    // 值
    private int val;
    // 判断是否为完整单词
    private boolean end;

    public PrefixTree(){
        // 小写字母,占用26位,如果有其它字母,这里需要调整
        this.next = new PrefixTree[26];
    }

    /**
     * 插入字符串
     */
    public void insert(String word, int val){
        // 先获取当前对象
        PrefixTree node = this;
        // 遍历字符,按照字符串中字符顺序建立字符链
        for(char w : word.toCharArray()){
            // 判断其如果还未创建当前字符对象,则创建对象
            if(null == node.next[w - 'a']){
                node.next[w - 'a'] = new PrefixTree();
            }
            // 指针后移
            node = node.next[w - 'a'];
        }
        // 保存结果
        node.val = val;
        // 标记完整字符路径
        node.end = true;
    }

    /**
     * 搜索字符串
     */
    public boolean search(String word){
        // 获取对象
        PrefixTree node = this;
        // 遍历字符串
        for(char w : word.toCharArray()){
            // 如果对应字符对象未创建,表示该字符串未添加
            if(null == node.next[w - 'a']){
                return false;
            }
            // 指针后移
            node = node.next[w - 'a'];
        }
        // 这里根据需要选择返回 val 或者 end
        return node.end;
    }
}

代码实现

class MapSum {
    private MapSum[] next;
    private int val;

    public MapSum() {
            this.next = new MapSum[26];
    }
    
    /**
     * 创建对应路径,保存结果
     */
    public void insert(String key, int val) {
        MapSum node = this;
            for(char k : key.toCharArray()){
                if(node.next[k - 'a'] == null){
                    node.next[k - 'a'] = new MapSum();
                }
                node = node.next[k - 'a'];
            }
            node.val = val;
    }
    
    /**
     * 获取该前缀及其后续节点的值的总和
     */
    public int sum(String prefix) {
        MapSum node = this;
        for(char pre : prefix.toCharArray()){
            // 没有该前缀对应路径则直接返回0
            if(node.next[pre - 'a'] == null){
                return 0;
            }else{
                // 指针后移
                node = node.next[pre - 'a'];
            }
        }
        // 求和
        return sum(node);
    }

    /**
     * 求得该节点及其后续节点的值的总和
     */
    public int sum(MapSum node){
        int sum = node.val;
        for(int i = 0; i < 26; ++i){
            // 节点不为空则继续递归向下搜索
            if(node.next[i] != null){
                sum += sum(node.next[i]);
            }
        }
        // 返回总和
        return sum;
    }
}

 复杂度分析

  •   时间复杂度:O(N)O(N)
  •   空间复杂度:O(N)O(N)

最后

文章有写的不好的地方,请大佬们不吝赐教,错误是最能让人成长的,愿我与大佬间的距离逐渐缩短!

如果觉得文章对你有帮助,请 点赞、收藏、关注、评论 一键四连支持,你的支持就是我创作最大的动力!!!

题目出处: leetcode-cn.com/problems/ma…