← 中文资源

简体中文版本

带权重的 Trie 自动补全

用大小写不敏感的 Trie 保存带权重词语,按权重和前缀返回自动补全候选。

阅读英文原文

功能说明

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 应返回空数组。