当前位置: 首页 > news >正文

【恋上数据结构】前缀树 Tire 学习笔记

Tire

需求分析

如何判断一堆不重复的字符串是否以某个前缀开头?

  • Set\Map 存储字符串(不重复)
  • 遍历所有字符串进行判断
  • 缺点:时间复杂度 O(n)

有没有更优的数据结构实现前缀搜索?

Tire(和 Tree 同音)

简介

  • Trie 也叫做字典树、前缀树 (Prefix Tree)、单词查找树。
  • Trie 搜索字符串的效率主要跟字符串的长度有关。

假设使用 Trie 存储 catdogdoggydoescastadd 六个单词,结果如下所示

在这里插入图片描述

接口设计

有两种设计方案:

  1. 第一种仅仅是存储字符串。(像 set 集合)
  2. 第二种是存储字符串的同时可以再存储一个 value(像 map 接口)

分析:

第二种设计方案更为通用,比如说我们要做一个通讯录,以某个人的姓名作为 key,然后以他的详细信息作为 value(其他电话号码、邮箱、生日等各种详细信息)

public interface Trie <V> {int size(); boolean isEmpty(); void clear(); boolean contains(String str); V add(String str, V value); V remove(String str); boolean starswith(String prefix);
}

在这里插入图片描述

Node 设计

孩子节点集合解析(HashMap<Character, Node<V>> children;):

  • key 相当于代表的是路径值,Character 字符类型可以是英文也可以是中文
  • value 是嵌套了当前节点下的所有子节点,方便后面节点值寻找
  • word:true 为已存储单词(红色),false 为非单词(蓝色)
    /*** Trie 中的节点类,包含父节点、孩子节点集合、字符、值以及表示是否为一个完整单词的标志。** @param <V> 值的类型*/private static class Node<V> {Node<V> parent; // 父节点HashMap<Character, Node<V>> children; // 孩子节点集合Character character; // 字符,为删除做准备V value; // 节点对应的值,也就是整个单词boolean word; // 是否为单词的结尾(是否为一个完整的单词)/*** 构造函数,初始化节点时需要指定父节点。(在添加节点时用到)** @param parent 父节点*/public Node(Node<V> parent) {this.parent = parent;}}

完整代码实现附注释

/*** Trie(字典树)数据结构,用于存储字符串集合,支持添加、查询、删除等操作。** @param <V> 值的类型*/
public class Trie<V> {/** Trie 中存储的单词数量 */private int size;/** 根节点 */private Node<V> root;/*** Trie 中的节点类,包含父节点、孩子节点集合、字符、值以及表示是否为一个完整单词的标志。** @param <V> 值的类型*/private static class Node<V> {Node<V> parent; // 父节点HashMap<Character, Node<V>> children; // 孩子节点集合Character character; // 字符,为删除做准备V value; // 节点对应的值,也就是整个单词boolean word; // 是否为单词的结尾(是否为一个完整的单词)/*** 构造函数,初始化节点时需要指定父节点。(在添加节点时用到)** @param parent 父节点*/public Node(Node<V> parent) {this.parent = parent;}}/*** 获取 Trie 中存储的单词数量。** @return Trie 中存储的单词数量*/public int size() {return size;}/*** 判断 Trie 是否为空。** @return 如果 Trie 为空,则返回 true;否则返回 false*/public boolean isEmpty() {return size == 0;}/*** 清空 Trie,将单词数量重置为 0。*/public void clear() {size = 0;root = null;}/*** 根据指定的键获取对应的值。** @param key 键* @return 如果键存在且是一个完整的单词,则返回对应的值;否则返回 null*/public V get(String key) {Node<V> node = node(key);return (node != null && node.word) ? node.value : null;}/*** 判断 Trie 是否包含指定的键。** @param key 键* @return 如果 Trie 包含指定的键且是一个完整的单词,则返回 true;否则返回 false*/public boolean contains(String key) {Node<V> node = node(key);return node != null && node.word;}/*** 添加键值对到 Trie 中。如果键已经存在,则更新对应的值;否则新增一个单词。** @param key   键* @param value 值* @return 如果添加的键已经存在,则返回对应的旧值;否则返回 null*/public V add(String key, V value) {keyCheck(key);// 创建根节点if (root == null) {root = new Node<>(null);}// 获取 Trie 根节点Node<V> node = root;// 获取键的长度int len = key.length();// 遍历键的每个字符for (int i = 0; i < len; i++) {// 获取当前字符char c = key.charAt(i);// 判断当前节点的孩子节点集合是否为空boolean emptyChildren = (node.children == null);// 获取当前字符对应的孩子节点Node<V> childNode = emptyChildren ? null : node.children.get(c);// 如果当前字符对应的孩子节点为空,说明该字符在当前节点的孩子节点集合中不存在if (childNode == null) {// 创建新的孩子节点,并将其加入到当前节点的孩子节点集合中childNode = new Node<>(node);childNode.character = c;// 判断孩子节点集合是否为空的同时,避免了每次都要创建新的 HashMap 对象,提高了效率node.children = emptyChildren ? new HashMap<>(16) : node.children;node.children.put(c, childNode);}// 将当前节点移动到其对应的孩子节点上,继续下一层的遍历node = childNode;}// 1 - 已经存在这个单词, 覆盖, 返回旧值if (node.word) {V oldValue = node.value;node.value = value;return oldValue;}// 2 - 不存在这个单词, 新增这个单词node.word = true;node.value = value;size++;return null;}/*** 移除 Trie 中的指定键。如果键存在且是一个完整的单词,将其从 Trie 中移除。** @param key 键* @return 如果键存在且是一个完整的单词,则返回对应的值;否则返回 null*/public V remove(String key) {Node<V> node = node(key);// 如果不是单词结尾,不用作任何处理if (node == null || !node.word) {return null;}size--;V oldValue = node.value;// 如果还有子节点if (node.children != null && !node.children.isEmpty()) {node.word = false;node.value = null;return oldValue;}// 没有子节点Node<V> parent = null;while ((parent = node.parent) != null) {parent.children.remove(node.character);if (parent.word || !parent.children.isEmpty()) {break;}node = parent;}return oldValue;}/*** 判断 Trie 是否包含指定前缀。** @param prefix 前缀* @return 如果 Trie 包含指定前缀,则返回 true;否则返回 false*/public boolean startsWith(String prefix) {return node(prefix) != null;}/*** 根据传入字符串,找到最后一个节点。* 例如输入 dog* 找到 g** @param key 键* @return 如果键存在,则返回对应的节点;否则返回 null*/private Node<V> node(String key) {keyCheck(key);Node<V> node = root;int len = key.length();for (int i = 0; i < len; i++) {if (node == null || node.children == null || node.children.isEmpty()) {return null;}char c = key.charAt(i);node = node.children.get(c);}return node;}/*** 检查键是否合法,不允许为空。** @param key 键*/private void keyCheck(String key) {if (key == null || key.length() == 0) {throw new IllegalArgumentException("key must not be empty");}}
}

总结

  1. Trie 的优点:搜索前缀的效率主要跟前缀的长度有关

  2. Trie 的缺点:需要耗费大量的内存(一个字符一个节点),因此还有待改进

  3. 更多 Trie 相关的数据结构和算法

    • Double-array Trie、Suffix Tree(后缀树)、Patricia Tree、Crit-bit Tree、AC 自动机

相关文章:

【恋上数据结构】前缀树 Tire 学习笔记

Tire 需求分析 如何判断一堆不重复的字符串是否以某个前缀开头&#xff1f; 用 Set\Map 存储字符串&#xff08;不重复&#xff09;遍历所有字符串进行判断缺点&#xff1a;时间复杂度 O(n) 有没有更优的数据结构实现前缀搜索&#xff1f; Tire&#xff08;和 Tree 同音&a…...

2023五岳杯量子计算挑战赛数学建模思路+模型+代码+论文

赛题思路&#xff1a;12月6日晚开赛后第一时间更新&#xff0c;获取见文末名片 “五岳杯”量子计算挑战赛&#xff0c;是国内专业的量子计算大赛&#xff0c;也是玻色量子首次联合移动云、南方科技大学共同发起的一场“企校联名”的国际竞赛&#xff0c;旨在深度融合“量子计算…...

Angular中的单向和双向数据绑定

1、单向数据绑定&#xff1a; 单向数据绑定是指数据从组件流向视图或从视图流向组件&#xff0c;但数据的流动是单向的。 在Angular中&#xff0c;主要有以下两种形式的单向数据绑定&#xff1a; 从组件到视图&#xff08;插值表达式&#xff09;&#xff1a; 使用插值表达式…...

【Vue】vue整合element

上一篇&#xff1a; vue项目的创建 https://blog.csdn.net/m0_67930426/article/details/134816155 目录 整合过程 使用&#xff1a; 整合过程 项目创建完之后&#xff0c;使用编译器打开项目 在控制器里输入如下命令 npm install element-ui 如图表示安装完毕 然后在…...

HarmonyOS应用开发者高级认证考试答案

一、判断题 云函数打包完成后&#xff0c;需要到AppGallery Connect创建对应函数的触发器才可以在端侧中调用&#xff08;错&#xff09;在column和Row容器组件中&#xff0c;aligntems用于设置子组件在主轴方向上的对齐格式&#xff0c;justifycontent用于设置子组件在交叉轴…...

6、Broker消息处理流程(六)

前面分析完Broker启动会启动RemotingServer服务同时会注册Processor处理器&#xff0c;接着分析Producer进行消息的发送&#xff0c;当Producer发送完消息后就得到Broker去接收Producer发送的消息了。 Producer发送给Broker消息时候&#xff0c;发送的请求code为SEND_MESSAGE(这…...

Clean 架构下的现代 Android 架构指南

Clean 架构下的现代 Android 架构指南 Clean 架构是 Uncle Bob 提出的一种软件架构&#xff0c;Bob 大叔同时也是 SOLID 原则的命名者。 Clean 架构图如下&#xff1a; 这张图描述的是整个软件系统的架构&#xff0c;而不是单体软件&#xff0c;其中至少包括服务端以及客户端…...

代码随想录算法训练营第四十六天| 139 单词拆分

目录 139 单词拆分 139 单词拆分 class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {vector<bool>dp(s.size() 1);//长度为i的字符串时能否成功拆分unordered_set<string>set(wordDict.begin(),wordDict.end());dp[0] t…...

IEEE期刊论文模板

一、模板下载 1、登陆IEEE作者中心Author Center 地址&#xff1a;Publish with IEEE Journals - IEEE Author Center Journals 2、点击“Download a template” 3、在弹出的模板下载页面点击IEEE模板选择器“IEEE Template Selector” 4、在弹出的模板选择器页面点击“Tran…...

上位机与PLC:ModbusTCP通讯之数据类型转换

前请提要: 从PLC读取的数值,不管是读正负整数还是正负浮点数,读取过来后都会变成UInt16,也就是Ushort类型 一、ushort(UInt16)转成 Int32 源代码方法: //ushort类型转Int32类型的方法private int ushortToInt32(ushort[] date, int start){//先进行判断,长度是否正确…...

界面控件DevExpress WPF导航组件,助力升级应用程序用户体验!(上)

DevExpress WPF的Side Navigation&#xff08;侧边导航&#xff09;、TreeView、导航面板组件能帮助开发者在WPF项目中添加Windows样式的资源管理器栏或Outlook NavBar&#xff08;导航栏&#xff09;&#xff0c;DevExpress WPF NavBar和Accordion控件包含了许多开发人员友好的…...

联合基于信息论的安全和隐蔽通信的框架

这个标题很帅 abstractintroductionsystem modelPROPOSED JOINT OPTIMIZATION OF ITS AND COVERT TRANSMISSION RATE信息论安全 (ITS)隐蔽通信需要(CC)Joint Information-Theoretic Secrecy and Covert Communication in the Presence of an Untrusted User and Warden 202…...

行业地位失守,业绩持续失速,科沃斯的故事不好讲

特劳特曾在《定位》一书中提到&#xff0c;为了在容量有限的消费者心智中占据品类&#xff0c;品牌最好的差异化就是成为第一&#xff0c;做品类领导者或开创者&#xff0c;销量遥遥领先&#xff1b;其次分化品类&#xff0c;做到细分品类的唯一&#xff0c;即细分品类的第一。…...

蓝桥杯:货物摆放--因数存到数组里的技巧--减少运算量的方法

小蓝有一个超大的仓库&#xff0c;可以摆放很多货物。 现在&#xff0c;小蓝有 n 箱货物要摆放在仓库&#xff0c;每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向&#xff0c;每箱货物的边都必须严格平行于长、宽、高。 小蓝希望所有的货物最终摆成一个大…...

我的创作纪念日——一年

机缘 初心始于对技术的热爱和分享知识的渴望。最初&#xff0c;我在一次练习中遇到了一些问题&#xff0c;通过解决这些问题并将解决方案记录下来&#xff0c;我意识到分享经验对自己和他人都非常有价值。于是&#xff0c;我开始在博客和社交平台上记录日常学习过程、撰写技术…...

Windows server 部署iSCSI共享磁盘搭建故障转移群集

在域环境下&#xff0c;在域控制器中配置iSCSI服务&#xff0c;配置共享网络磁盘&#xff0c;在节点服务器使用共享磁盘&#xff0c;并在节点服务器中搭建故障转移群集&#xff0c;实现故障转移 环境准备 准备3台服务器&#xff0c;配置都是8g2核&#xff0c;50g硬盘&#xf…...

2023年山东省职业院校技能大赛信息安全管理与评估二三阶段样题

2023年山东省职业院校技能大赛信息安全管理与评估二三阶段 样题 第二阶段 模块二 网络安全事件响应、数字取证调查、应用程序安全 一、竞赛内容 Geek极安云科专注技能竞赛技术提升&#xff0c;基于各大赛项提供全面的系统性培训&#xff0c;拥有完整的培训体系。团队拥有曾…...

数据结构——栈与栈排序

栈的特性 栈是一种遵循后进先出&#xff08;LIFO&#xff09;原则的数据结构。其基本操作包括&#xff1a; push&#xff1a;将元素添加到栈顶。pop&#xff1a;移除栈顶元素。peek&#xff1a;查看栈顶元素&#xff0c;但不移除。 栈排序的原理 栈排序的核心是使用两个栈&…...

Java Web应用小案例 - 实现用户登录功能

文章目录 一、使用纯JSP方式实现用户登录功能&#xff08;一&#xff09;项目概述&#xff08;二&#xff09;实现步骤1、创建Web项目2、创建登录页面 二、使用JSPServlet方式实现用户登录功能三、使用JSPServletDB方式实现用户登录功能 一、使用纯JSP方式实现用户登录功能 &a…...

Excel——多列合并成一列的4种方法

Excel怎么将多列内容合并成一列&#xff1f; 怎么将多个单元格的内容连接起来放在一个单元格里&#xff1f; 比如下图&#xff0c;要将B、C、D列的内容&#xff0c;合并成E列那样&#xff0c;该怎么做呢&#xff1f; △图1 本文中&#xff0c;高潜老师将给大家介绍 4种 将多…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...