【算法】前缀树

86 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

一、前言

Trie 树(又称字典树、前缀树)一棵从二叉树衍生出来的多叉树

  • 核心思想是空间换时间: 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

前缀树-2022-08-0916-57-07.png

前缀树的3个基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。

前缀树模板,可看下面题目1:实现前缀树。


二、题目

(1)实现前缀树Trie(中)

LeetCode 208

题干分析

这个题目说的是,你要实现一棵前缀树(Trie),其中包含 insert, searchstartsWith 三个方法。

  • insert: 用于插入一个单词
  • search: 用于搜索一个单词是否存在于前缀树中
  • startsWith:用于判断前缀树中是否存在一个包含给定前缀的单词。

注意:所有的输入都是非空字符串,并且只包含小写字母。

# 举个栗子:
# 前缀树中有 bbc\
  b
   \
    b
     \
      c
      
# Search 搜索
bbc 能搜索到
bb  搜索不到
​
# startWith 
bb 能查询到

思路解法

首先,定义前缀树的节点 Node 一棵 26叉树。

private class TrieNode {
    boolean endOfWord;    // 是否是一个单词结尾
    TrieNode [] children; // 指向 26个 子节点
    TrieNode() {
        this.endOfWord = false;
        this.children = new TrieNode[26];
    }
}

举个栗子: 字符串 bbc

  1. 新增操作

前缀树-2022-08-0917-14-41.png

  1. 搜索单词: 根据树一个节点一个节点搜索

    最后,单词的最后一个字母对应的节点的 endOfWord 需要为 true

  2. 搜索前缀: 根据树一个节点一个节点搜索

    不需要看节点的 endOfWord 是否为 true

完整代码如下:

// Faster: 87.89%
class Trie {
​
    private class TrieNode {
        boolean endOfWord;    // 是否是一个单词结尾
        TrieNode [] children; // 指向 26个 子节点
        TrieNode() {
            this.endOfWord = false;
            this.children = new TrieNode[26];
        }
    }
    private TrieNode root;
​
    public Trie() {
        this.root = new TrieNode();
    }
    // 新增
    public void insert(String word) {
        TrieNode cur = root;
        for (int i = 0; i < word.length(); ++i) {
            int idx = word.charAt(i) - 'a';
            if (cur.children[idx] == null) cur.children[idx] = new TrieNode();
            cur = cur.children[idx];
        }
        cur.endOfWord = true;
    }
    // 搜索
    public boolean search(String word) {
        TrieNode node = searchEndNode(word);
        return node != null && node.endOfWord;
    }
    // 搜索节点
    private TrieNode searchEndNode(String str) {
        TrieNode cur = root;
        for (int i = 0; i < str.length() && cur != null; ++i) {
            int idx = str.charAt(i) - 'a';
            cur = cur.children[idx];
        }
        return cur;
    }
    // 搜索前缀
    public boolean startsWith(String prefix) {
        TrieNode node = searchEndNode(prefix);
        return node != null;
    }
}