功能说明
Trie 把单词逐字符存入树结构,每个节点用 Map 保存子节点,并用 terminal 标记单词结束。每个词还带一个权重,用于让常用词排在前面。suggest(prefix, limit) 先找到前缀对应的节点,再深度遍历收集所有以该前缀开头的单词,按权重降序、同权重按字典序排序后截取前若干个。
默认不区分大小写,插入和查询都会转成小写,因此 has("APP") 能匹配 app。startsWith 只判断前缀是否存在,不要求构成完整单词;wordCount 统计唯一单词数量,重复插入同一单词只会更新权重,不会重复计数。它适合搜索框建议、命令补全和标签提示。
关键代码
查找前缀后递归收集终止节点,最后统一排序:
const matches = [];
this.#collect(node, normalized, matches);
matches.sort(
(left, right) =>
right.weight - left.weight || left.word.localeCompare(right.word),
);
return matches.slice(0, limit).map((entry) => entry.word);
#collect 在遇到 terminal 时记录当前前缀和权重,再继续遍历所有子节点,因此能收集完整子树。
使用注意
大小写不敏感模式下返回的候选也是小写形式,若需要保留原始大小写,应关闭该选项或在节点中额外保存展示文本。权重必须是非负有限数;负权重虽然不会报错但会排在后面,容易造成误解。limit 必须为正整数,无法通过 0 获取空数组。
递归收集在超大词典或极长前缀下可能占用较多栈空间,可以改为显式栈。suggest 每次都会遍历整个前缀子树,时间复杂度与前缀下的单词数量成正比;对高频查询可以缓存结果。删除单词没有直接支持,需要额外实现节点清理。
验证方式
示例插入 apple、app、apricot、banana、appetite、application 及不同权重,查询 app 应返回 app, application, apple, appetite,顺序由权重决定。has("APP") 和 startsWith("APR") 都应为 true,wordCount 为 6。查询不存在的前缀 zz 应返回空数组。