811 字
4 分钟
Trie树
Trie tree: 前缀树, 又称字典树, 是N叉树的特殊形式.
基本概念
Trie树是用来解决一组字符串集合中快速查找某个字符串的问题.
- 数据结构
[image:999FE3AB-7090-4EAF-81D8-0037257B7FCE-894-00000DFC95BA724D/280fbc0bfdef8380fcb632af39e84b32.jpg]
* 根节点: 不包含任何信息* 每个节点: 树中的每个节点表示一个字符串中的一个字符, 从根节点到红色节点的一条路径表示一个字符串.注意: 红色节点并不都是叶子节点
Trie 树的本质,就是利用字符串之间的公共前缀,将重复的前缀合并在一起
如何表示一个前缀树
- 数组
用数组存储每一层的节点, 例如我们要存储的都是a~z的字母, 那么就可以声明一个长度为26的数组. 当我们要找’c’时, 就可以通过'c'-'a'的值来访问.
优势: 访问便捷 略势: 空间浪费
- Map
用Map存储每一层的节点. key为这一层的字符, value为字符的子节点.
优势: 节省空间 略势: 比数组访问慢
补充. Trie树中, 每个节点除了存储字符外, 当需要判断当前节点是否为一个完整单词时, 需要别的字段来辅助, 比如每个节点添加一个 bool 变量, 用来判断是否为一个完整单词.
注意: 完整单词的节点不一定是叶子节点. 比如: hi, hight, 中的i, 不是叶子节点, 但是hi是一个完整的单词.
实现一个前缀树
package trie
type Trie struct { isWorld bool child map[byte]*Trie}
/** Initialize your data structure here. */func Constructor() Trie { return Trie{child: make(map[byte]*Trie)}}
/** Inserts a word into the trie. */func (this *Trie) Insert(word string) { node := this child := this.child for _, char := range word { if c, exists := child[byte(char)]; exists { node = c child = c.child } else { node = &Trie{ isWorld: false, child: make(map[byte]*Trie), } child[byte(char)] = node child = node.child } } node.isWorld = true}
/** Returns if the word is in the trie. */func (this *Trie) Search(word string) bool { isWorld := false child := this.child for _, char := range word { if c, exists := child[byte(char)]; !exists { isWorld = false break } else { isWorld = c.isWorld child = c.child } } return isWorld}
/** Returns if there is any word in the trie that starts with the given prefix. */func (this *Trie) StartsWith(prefix string) bool { child := this.child for _, char := range prefix { if c, exists := child[byte(char)]; !exists { return false } else { child = c.child } } return true}实际应用
前缀树广泛应用于 存储 字符串和 检索 关键字,特别是和 前缀 相关的关键字。
- 自动补全: 如搜索框的自动补全
- 拼写检查
实际应用二
- 加速深度优先搜索 有时,我们会用前缀树来加速深度优先搜索。特别是当我们使用深度优先搜索解决文字游戏类问题的时候。
- 存储其他数据类型
- 还有一些其他应用,例如 IP 路由(最长前缀匹配)
参考
- 力扣(LeetCode)
#01 Blog/算法相关#