定义
Trie 树是一种有序树,用于存储字符串,其中每个节点都代表输入字符串的一个字符。从根节点到任意一个叶子节点的路径上所包含的字符序列构成了一个字符串。
节点结构
每个节点通常包含以下部分:
- 字符:代表该节点对应的字符。
- 子节点引用:一个指向其子节点的数组或哈希表,数组或哈希表的索引通常是字符集中的字符。例如,如果存储的是小写字母组成的字符串,那么数组大小为26。
- 标志位:表示该节点是否是一个字符串的结尾。有时候也会附加其他信息,如该字符串的频次等。
根节点
根节点通常不包含任何字符信息,它是一个虚拟节点,用于连接所有的字符串的第一个字符。
插入操作
当插入一个新的字符串时,会从根节点开始,沿着字符路径向下寻找。如果路径上的某个字符已经存在,则继续沿着该路径;如果某个字符不存在,则创建一个新的节点,并将该字符作为新节点的内容。
查找操作
查找一个字符串时,同样从根节点开始,沿着字符路径向下遍历。如果能够一直遍历到字符串的最后一个字符,并且该字符所在节点标记为字符串结尾,则表示找到了该字符串。
删除操作
删除一个字符串时,需要从根节点开始,沿着字符路径向下寻找。如果找到了字符串的最后一个字符,并且该字符所在的节点没有其他子节点,则可以删除该节点及其以上的无用节点。如果该节点还有其他子节点,则仅将该节点标记为非字符串结尾。
特点
- 高效查找:Trie 树的时间复杂度为 O(L),L 是要查找的字符串的长度。这是因为每次查找都沿着字符路径进行一次比较。
- 节省空间:共享公共前缀的字符串只需存储一次前缀,这使得 Trie 树在存储大量字符串时更加节省空间。
- 方便排序:因为 Trie 树是按照前缀进行存储的,所以可以方便地对字符串进行排序。
应用
- 自动补全:搜索引擎、文本编辑器等工具中的自动补全功能。
- 拼写检查:用于检查输入的单词是否存在于词典中。
- IP 路由:用于查找最长前缀匹配,例如在路由器的查找表中。
- 词频统计:用于统计文本中单词出现的次数。
示例
假设我们要构建一个 Trie 树来存储字符串集合 {“cat”, “car”, “dog”, “caterpillar”}:
-
插入 “cat”:
- 根 -> c -> a -> t(结束)
-
插入 “car”:
- 根 -> c -> a -> r(结束)
-
插入 “dog”:
- 根 -> d -> o -> g(结束)
-
插入 “caterpillar”:
- 继续扩展根 -> c -> a -> t(非结束)-> e -> r -> p -> i -> l -> l -> a -> r(结束)
最终的 Trie 树看起来像这样:
(root)
|
|___ 'c' : {'a' : {'t' : {'': {}} // cat, caterpillar
'r' : {'': {}} // car
},
'd' : {'o' : {'g' : {'': {}}}} // dog
}
在这个 Trie 树中,“caterpillar”和“cat”共享相同的前缀“cat”,而“car”和“cat”也共享相同的前缀“ca”。这种共享前缀的方式使得 Trie 树成为一种高效的字符串存储结构。