【算法】探索经典字典树(Trie):详细图文指南——js实现

129 阅读4分钟

字典树(Trie)是一种用于快速检索字符串的树形数据结构。它广泛应用于自动补全、拼写检查、IP路由(最长前缀匹配)、T9输入法等领域。

1. 什么是字典树(Trie)?

字典树是一种树形结构,用于存储字符串。每个节点代表一个字符串中的字符,路径从根节点到某一节点代表一个字符串。以下是一个简单的例子:

在这个例子中,插入了三个单词:"cat", "car", "dog"。我们可以看到相同前缀的单词共享了相同的路径。

2. 字典树的结构

字典树的每个节点包含以下几部分:

  • 子节点:节点的子节点可以是字母表中的任何字符。
  • 是否为单词结束标志:一个布尔值,表示是否为一个单词的结尾。

示例结构

考虑插入单词 "cat" 和 "car" 后的字典树:

        (root)
       /    \
      c      d
     /       \
    a         o
   / \         \
  t   r         g
                \
                 e

3. 操作字典树

插入单词

插入单词时,从根节点开始,依次插入每个字符。如果字符路径不存在,则创建新的节点。最后,将最后一个字符节点标记为单词的结束。

示例:插入 "cat" 和 "car"

  1. 插入 "cat":
        (root)
         |
         c
         |
         a
         |
         t (end)
  1. 插入 "car":
        (root)
         |
         c
         |
         a
        / \
       t   r (end)
     (end)

查找单词

查找单词时,从根节点开始,依次检查每个字符节点是否存在。如果能找到所有字符并且最后一个字符节点标记为单词的结束,则单词存在。

示例:查找 "car"

  1. 从根节点出发,依次检查 c -> a -> r,最后一个字符节点标记为单词的结束,返回 True。

删除单词

删除单词时,从根节点开始,依次查找每个字符节点。如果能找到所有字符且最后一个字符节点标记为单词的结束,则可以删除该单词。删除时需要注意是否需要删除路径上的中间节点。

示例:删除 "car"

  1. 从根节点出发,依次找到 c -> a -> r。
  2. 取消 r 的结束标志。如果 r 没有子节点,则删除 r,并依次检查并删除无用的中间节点 a -> c。
        (root)
         |
         c
         |
         a
         |
         t (end)

4. 示例代码

以下是 js 实现字典树的基本操作的代码示例:

// 定义一个TrieNode类来表示字典树的节点
class TrieNode {
    constructor() {
        this.children = {}; // 存储子节点
        this.isEndOfWord = false; // 标记是否为单词的结束
    }
}

// 定义Trie类来表示字典树
class Trie {
    constructor() {
        this.root = new TrieNode(); // 初始化字典树的根节点
    }

    // 插入单词的方法
    insert(word) {
        let node = this.root; // 从根节点开始
        for (let char of word) { // 遍历单词中的每个字符
            if (!node.children[char]) { // 如果当前字符的子节点不存在
                node.children[char] = new TrieNode(); // 创建一个新的TrieNode
            }
            node = node.children[char]; // 移动到子节点
        }
        node.isEndOfWord = true; // 标记单词的结束
    }

    // 查找单词的方法
    search(word) {
        let node = this.root; // 从根节点开始
        for (let char of word) { // 遍历单词中的每个字符
            if (!node.children[char]) { // 如果当前字符的子节点不存在
                return false; // 单词不存在
            }
            node = node.children[char]; // 移动到子节点
        }
        return node.isEndOfWord; // 返回是否为单词的结束
    }

    // 删除单词的方法
    delete(word) {
        const _delete = (node, word, depth) => {
            if (!node) { // 如果节点不存在
                return false;
            }

            if (depth === word.length) { // 如果达到单词的末尾
                if (!node.isEndOfWord) { // 如果当前节点不是单词的结束
                    return false;
                }
                node.isEndOfWord = false; // 取消单词结束标志
                return Object.keys(node.children).length === 0; // 检查是否需要删除当前节点
            }

            const char = word[depth]; // 获取当前字符
            if (!_delete(node.children[char], word, depth + 1)) { // 递归删除子节点
                return false;
            }

            delete node.children[char]; // 删除子节点
            return Object.keys(node.children).length === 0; // 检查当前节点是否还有其他子节点
        };

        _delete(this.root, word, 0); // 从根节点开始删除单词
    }
}


使用示例

const trie = new Trie(); // 创建一个新的字典树实例
trie.insert("cat"); // 插入单词 "cat"
trie.insert("car"); // 插入单词 "car"
trie.insert("dog"); // 插入单词 "dog"

console.log(trie.search("cat"));  // true,找到单词 "cat"
console.log(trie.search("car"));  // true,找到单词 "car"
console.log(trie.search("cow"));  // false,未找到单词 "cow"

trie.delete("car"); // 删除单词 "car"
console.log(trie.search("car"));  // false,单词 "car" 已被删除


结语

字典树(Trie)是一种强大的数据结构,适用于各种需要快速字符串检索的应用场景。通过这篇指南,你应该能够理解并实现基本的字典树操作。希望这篇博客对你有所帮助!