当前位置: 首页 > 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种 将多…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...