leetcode
leetcode 1601 ~ 1650
实现 Trie (前缀树) II

实现 Trie (前缀树) II

难度:

标签:

题目描述

代码结果

运行时间: 177 ms, 内存: 28.9 MB


/*
题目思路:
Trie树是一种树形结构,用于高效地存储和检索字符串集合中的键。
主要操作包括插入一个字符串、搜索一个字符串和搜索具有某前缀的字符串。
在Trie树中,每个节点表示字符串中的一个字符,根节点表示空字符。
使用Java Stream的方式来实现Trie树的功能。
*/

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

class TrieNode {
    public Map<Character, TrieNode> children;
    public boolean isEndOfWord;
    public TrieNode() {
        children = new HashMap<>();
        isEndOfWord = false;
    }
}

class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    // 插入一个单词到Trie树
    public void insert(String word) {
        TrieNode node = root;
        word.chars().mapToObj(c -> (char) c).forEach(c -> {
            node.children.putIfAbsent(c, new TrieNode());
            node = node.children.get(c);
        });
        node.isEndOfWord = true;
    }

    // 搜索Trie树中是否存在某个单词
    public boolean search(String word) {
        TrieNode node = root;
        return word.chars().mapToObj(c -> (char) c).allMatch(c -> {
            if (node.children.containsKey(c)) {
                node = node.children.get(c);
                return true;
            } else {
                return false;
            }
        }) && node.isEndOfWord;
    }

    // 搜索Trie树中是否存在以某个前缀开头的单词
    public boolean startsWith(String prefix) {
        TrieNode node = root;
        return prefix.chars().mapToObj(c -> (char) c).allMatch(c -> {
            if (node.children.containsKey(c)) {
                node = node.children.get(c);
                return true;
            } else {
                return false;
            }
        });
    }
}

解释

方法:

这个题解实现了一个 Trie (前缀树) 数据结构,并支持插入、查询和删除字符串的操作。每个节点包含一个映射表 children 来保存子节点,一个 count 变量记录经过此节点的单词数量,和一个 endCount 变量记录以此节点结尾的单词数量。在 insert 方法中,遍历插入的单词的每个字符,如果字符不存在于当前节点的 children 中,则创建一个新的 Trie 节点。同时更新经过的每个节点的 count 值。countWordsEqualTo 方法通过遍历单词找到对应的节点并返回其 endCount 值,表示完全匹配该单词的数量。countWordsStartingWith 方法遍历前缀找到对应节点并返回其 count 值,表示以该前缀开始的单词数量。erase 方法用于删除一个单词,它减少经过每个节点的 count 值和最终节点的 endCount 值。

时间复杂度:

O(m)

空间复杂度:

O(∑w)

代码细节讲解

🦆
在 `countWordsEqualTo` 方法中,如果某字符不存在于 Trie 中,直接返回 0。这种设计是否意味着所有 Trie 操作都假设输入字符串完全由已知字符集构成?
不完全是这样。这种设计只是表明,如果在查询过程中遇到 Trie 中不存在的字符,则可以断定 Trie 中不存在以此字符为部分的完整单词。这并不意味着所有 Trie 操作都假设输入字符串完全由已知字符集构成,而是说明 Trie 对于未知字符的查询将返回 0,表示这样的单词不存在。在 Trie 的其他操作中,如插入或删除,会动态处理字符集,添加或修改 Trie 的结构。
🦆
Trie 的 `count` 和 `endCount` 变量如何准确反映删除操作后的单词频率?即在某些节点的 `count` 可能减少到零的情况下,这些节点是否还保留在 Trie 中?
在 Trie 中,`count` 和 `endCount` 变量确实用来跟踪经过每个节点的单词数量和以该节点结尾的单词数量。当执行删除操作时,这些值会相应减少。如果某节点的 `count` 值减少到零,理论上这意味着没有任何单词再通过这个节点。尽管如此,该节点可能仍然保留在 Trie 中,除非额外实现节点的清除逻辑。这通常涉及到检查节点是否还有子节点,以及 `endCount` 是否为零,如果没有子节点且 `endCount` 为零,则可以安全地移除该节点。没有这样的清除逻辑,即使 `count` 为零的节点也会保留在 Trie 中。

相关问题