java30-前缀树

186 阅读2分钟

HashMap<K, V> 的优势是能够在 O(1) 时间通过键查找对应的值,但要求键的类型 K 必须是「可哈希」的;而 TreeMap<K, V> 的特点是方便根据键的大小进行操作,但要求键的类型 K 必须是「可比较」的。

  • 基本的二叉树节点
calss TreeNode{
    int val;
    TreeNide left, right;
}

  • 多叉树的节点
class TreeNode{
    int val;
    TreeNode[] children;  //因为有多个孩子,用节点数组来表示
    //children数组存储指向孩子节点的指针
}
  • Trie 前缀树
/* Trie 树节点实现 */
class TrieNode<V> {
    V val = null; 
    TrieNode<V>[] children = new TrieNode[256];
}

但是和之前的普通多叉树节点不同,TrieNode 中 children 数组的索引是有意义的,代表键中的一个字符
比如说 children[97] 如果非空,说明这里存储了一个字符 'a',因为 'a' 的 ASCII 码为 97。

我们的模板只考虑处理 ASCII 字符,所以 children 数组的大小设置为 256。不过这个可以根据具体问题修改,比如改成更小的数组或者 HashMap<Character, TrieNode> 都是一样的效果。

典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计

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

给出一组单词,inn, int, at, age, adv,ant, adv 我们可以得到下面的Trie:

1.jpeg

建造树,逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add

考察前缀"a",发现边a已经存在。于是顺着边a走到节点a。

考察剩下的字符串"dd"的前缀"d",发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad

TrieNode 节点本身只存储 val 字段,并没有一个字段来存储字符,字符是通过子节点在父节点的 children 数组中的索引确定的

形象理解就是,Trie 树用「树枝」存储字符串(键),用「节点」存储字符串(键)对应的数据(值)。所以我在图中把字符标在树枝,键对应的值 val 标在节点上