不用 map[string]node 因其将整个字符串作 key,浪费内存且不支持字符级遍历;trie 需单字符跳转,应使用 rune/byte 索引的数组或 map,如 [26]node 或 map[rune]*node。

为什么不用 map[string]*Node 做 Trie 节点
因为 map[string]*Node 看似直观,但实际会把整个字符串当 key 存一次,既浪费内存又无法支持按字符逐级遍历。Trie 的核心是「单字符跳转」,不是「子串匹配」。
正确做法是用数组或 map 按 rune(或 byte)索引:children[26](小写英文)或 map[rune]*Node(通用 Unicode)。前者快且省内存,后者灵活但有哈希开销。
- 英文场景优先用
[26]*Node,用c - 'a'算下标,注意判c >= 'a' && c - 含中文、emoji 等必须用
map[rune]*Node,否则越界或静默丢字符 - 别用
map[byte]*Node处理 UTF-8 字符串——"你好"[0]是乱码首字节,不是完整字符
Insert 和 Search 里要不要显式处理空字符串
要。空字符串 "" 是合法前缀,也是有效单词。不处理会导致 Search("") == false,即使你刚 Insert("") 过。
关键在节点结构里加一个 isEnd bool 字段,而不是靠 children 是否为空判断。
-
Insert("")就是把根节点的isEnd = true -
Search("")直接返回根节点的isEnd,不进循环 - 否则你会写出类似
for _, c := range word { ... }这种对空串无效的逻辑
Prefix search 性能差?检查是否提前 return 了
很多人写 StartsWith(prefix) 时,在遍历中途发现某个字符不存在就直接 return false,这没问题;但容易漏掉「走完所有字符后没确认是否到达有效节点」。
错误写法:走到最后一个字符对应节点就 return true —— 实际上那个节点可能只是中间跳板,根本没设 isEnd,更别说它本就不需要是终点。
-
StartsWith(prefix)只需成功走到末尾节点,不管isEnd,所以最后 return true 即可 -
Search(word)必须走到末尾 *且* 该节点isEnd == true - 别复用同一段遍历逻辑混用两种语义,容易错位
golang 中 rune vs byte 对 Trie 的实际影响
Go 字符串底层是 byte 序列,但用户语义是 rune(Unicode 码点)。Trie 如果按 byte 走,遇到 UTF-8 多字节字符就会断在中间,比如 "好" 占 3 个 byte,"好"[0] 不是 '好',是 0xe5,查不到任何有意义的子节点。
所以只要业务涉及非 ASCII 字符,就必须用 for _, r := range word 拆 rune,而不是 for i := range word 或 []byte(word)。
- 用
range遍历字符串自动解码 UTF-8,r是完整 rune - 如果确定只跑 ASCII(如 HTTP header key),可用
word[i]当 byte 用,省点开销 - 混合场景?别省,统一用 rune,避免后期字符集扩展时出 bug
事情说清了就结束。Trie 看似简单,但字符编码、空串语义、前缀/全词区分这三点,线上一跑就露馅。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











