前缀Trie 实现类似百度搜索的提示的站内搜索

2023-10-22 19:40

本文主要是介绍前缀Trie 实现类似百度搜索的提示的站内搜索,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
实现一个站内导航搜索
在这里插入图片描述

引子

前缀Trie, 又叫字符Tire, trie来自单词retrieval, 一开始念作tree,后来改念try, 毕竟它与树是不一样的东西。网上许多文章都搞混了trie与树。 trie是通过”边“来储存字符的一种树状结构,所谓边就是节点与节点间的连接。trie每条边只能存放一个字符。

在这里插入图片描述

它与hash树很相似,或者说它是哈希树的变种,哈希树是用边来存放一个整数(可以是一位数或两位数)。前树Tire与哈希树都是多叉树,换言之,父节点有N个子节点。
前缀Tire主要用于字符串的快速检索,查询效率比哈希表高
前缀Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
前缀Trie树也有它的缺点, 假定我们只对字母与数字进行处理,那么每个节点至少有52+10个子节点。为了节省内存,我们可以用链表或数组。在JS中我们直接用数组,因为JS的数组是动态的,自带优化。

基本性质

  1. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符
  2. 从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串
  3. 每个节点的所有子节点包含的字符都不相同

程序实现

class TrieNode {constructor() {this.numPass = 0;//有多少个单词经过这节点this.numEnd = 0; //统计单词的个数this.edges = [];this.value = ""; //value为单个字符this.isEnd = false;}
}/*** 前缀树* -----* @see https://segmentfault.com/a/1190000013018855*/
class Trie {constructor() {this.root = new TrieNode();}insert(word) {var cur = this.root;for (var i = 0; i < word.length; i++) {var c = word.charCodeAt(i);c -= 48; //减少“0”的charCodevar node = cur.edges[c];if (node == null) {var node = (cur.edges[c] = new TrieNode());node.value = word.charAt(i);node.numPass = 1; //有N个字符串经过它} else {node.numPass++;}cur = node;}cur.isEnd = true; //樯记有字符串到此节点已经结束cur.numEnd++; //这个字符串重复次数return true;}/*** 先序遍历* @param {function} cb */preTraversal(cb) {function preTraversalImpl(root, str, cb) {cb(root, str);for (let i = 0, n = root.edges.length; i < n; i++) {let node = root.edges[i];if (node) {preTraversalImpl(node, str + node.value, cb);}}}preTraversalImpl(this.root, "", cb);}}

测试

var trie = new Trie();
trie.insert("I");
trie.insert("Love");
trie.insert("武昭");
trie.insert("武昭");
trie.insert("武昭在哪里???");
trie.insert("武昭在干什么???");
trie.insert("武昭🙂???");
trie.insert("武昭没吃饭???");
trie.insert("China");
trie.insert("China");
trie.insert("China");
trie.insert("China");
trie.insert("xiaoliang");
trie.insert("xiaoliang");
trie.insert("man");
trie.insert("handsome");
trie.insert("love");
trie.insert("Chinaha");
trie.insert("her");
trie.insert("know");console.log("包含武(包括本身)前缀的单词及出现次数:");
console.log("---------------start--------------------")
var map = {}
trie.preTraversal(function (node, str) {if (str.indexOf("武") === 0 && node.isEnd) {map[str] = node.numEnd}
})
for (var i in map) {console.log(i + " 出现了" + map[i] + " 次")
}

在这里插入图片描述

在antDesignPro中使用

在这里插入图片描述

代码实现

trie.js

import config from '../../config/config'/*** 引入config路由生成用name做key,path做value的字典,用于站内检索* -----* @param {*} list*/
const genDict = (list) => {const res = {}list.forEach(item => {const dfs = data => {if (typeof data.name === 'string') {//TODO 需要传递参数的路由排除res[data.name] = data.path;}data.routes && data.routes.forEach(child => dfs(child));}dfs(item);})return res
}class TrieNode {constructor() {this.numPass = 0;//有多少个单词经过这节点this.numEnd = 0; //统计单词的个数this.edges = [];this.value = ""; //value为单个字符this.isEnd = false;}
}/*** 搜索前缀树* -----* 参考修改* @see https://segmentfault.com/a/1190000013018855*/
class Trie {constructor() {this.root = new TrieNode();}insert(word) {var cur = this.root;for (var i = 0; i < word.length; i++) {var c = word.charCodeAt(i);c -= 48; //减少“0”的charCodevar node = cur.edges[c];if (node == null) {var node = (cur.edges[c] = new TrieNode());node.value = word.charAt(i);node.numPass = 1; //有N个字符串经过它} else {node.numPass++;}cur = node;}cur.isEnd = true; //樯记有字符串到此节点已经结束cur.numEnd++; //这个字符串重复次数return true;}/*** 先序遍历* -----* @param {function} cb*/preTraversal(cb) {function preTraversalImpl(root, str, cb) {cb(root, str);for (let i = 0, n = root.edges.length; i < n; i++) {let node = root.edges[i];if (node) {preTraversalImpl(node, str + node.value, cb);}}}preTraversalImpl(this.root, "", cb);}
}var trie = new Trie();
const nameMap = genDict(config.routes)
for (const iterator of Object.keys(nameMap)) {trie.insert(iterator);
}/*** trie是一个 用路由的name做key,路由的path做value 生成的 搜索前缀树对象* -----* 使用示例:* 查找已“武”开头的字符有哪些* var map = {}* trie.preTraversal(function (node, str) {*   if (str.indexOf("武") === 0 && node.isEnd) {*     map[str] = node.numEnd*   }* })* for (var i in map) {*   console.log(i + " 出现了" + map[i] + " 次")* }** 1.调用对象的先序遍历方法传入一个函数,就可以统计前缀匹配的单词*/export { nameMap, trie }

引入config路由生成用name做key,path做value的字典,用于站内检索

RightContent.jsx

在HeaderSearch中使用
在这里插入图片描述


使用方式:
在onSearch事件触发后调用这个匿名函数,匿名函数的逻辑同上边测试代码,调用trie的先序遍历传入要匹配的value,会生成匹配到的值是一个map,遍历map生成相关的数组填入AutoComplete组件的dataSource属性中,就实现了搜索展示功能。
搜索消耗还是挺大的,使用Debounce限制一下搜索的频率(这里用的lodash的)

后续完善

  1. 有一些不能直接搜索进入的路由需要在生成字典的时屏蔽掉

在这里插入图片描述

  1. 搜索进入选中相关的key完善,现在直接就进入了不能选中Key,因为我们为了实现三级菜单重写了这个Menu的一点逻辑。

参考摘抄

https://segmentfault.com/a/1190000013018855

搬家语雀了

csdn编辑器太难用了

原文语雀
https://www.yuque.com/wuzhao/kb/wgo04g

这篇关于前缀Trie 实现类似百度搜索的提示的站内搜索的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/263224

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4