Trie树(前缀树)的实现与应用

Trie树,也被称为前缀树,是一种用于处理字符串的数据结构。它可以高效地进行字符串的插入、删除和搜索操作,并且能够快速找到具有相同前缀的字符串。本篇博客将详细介绍Trie树的实现原理和应用场景,并给出Java代码示例。

Trie树的基本结构

Trie树的基本结构由节点和边组成,每个节点表示一个字符,每条边表示一个字符的连接。从根节点到叶子节点的路径表示一个完整的字符串。
在Java代码示例中,我们定义了两种Trie树的实现方式。 Trie1 使用数组作为节点的存储结构, Trie2 使用哈希表作为节点的存储结构。两种实现方式在时间复杂度上没有本质的差异,只是在空间复杂度上有所不同。

Trie树的基本操作

Trie树的基本操作包括插入、删除、搜索和前缀匹配。

插入操作

插入操作用于将一个字符串插入到Trie树中。具体步骤如下:

  1. 从根节点开始,遍历字符串的每个字符。
  2. 对于每个字符,判断是否存在与之对应的子节点。
    • 如果不存在,则创建一个新的节点,并将其作为当前节点的子节点。
    • 如果存在,则将当前节点指向该子节点。
  3. 在遍历完所有字符后,将当前节点的 end 计数加一,表示该字符串的插入次数。

删除操作

删除操作用于从Trie树中删除一个字符串。具体步骤如下:

  1. 首先通过搜索操作判断该字符串是否存在于Trie树中。
  2. 如果存在,则从根节点开始,遍历字符串的每个字符。
  3. 对于每个字符,判断是否存在与之对应的子节点。
    • 如果存在,则将当前节点指向该子节点。
    • 如果不存在,则返回,表示该字符串不存在于Trie树中。
  4. 在遍历完所有字符后,将当前节点的 end 计数减一。
  5. 如果当前节点的 pass 计数为0,表示该节点没有其他字符串经过,可以将其删除。

搜索操作

搜索操作用于判断一个字符串在Trie树中出现的次数。具体步骤如下:

  1. 从根节点开始,遍历字符串的每个字符。
  2. 对于每个字符,判断是否存在与之对应的子节点。
    • 如果存在,则将当前节点指向该子节点。
    • 如果不存在,则返回0,表示该字符串不存在于Trie树中。
  3. 在遍历完所有字符后,返回当前节点的 end 计数,即表示该字符串在Trie树中出现的次数。

前缀匹配操作

前缀匹配操作用于统计所有以给定前缀开头的字符串的出现次数。具体步骤如下:

  1. 从根节点开始,遍历前缀字符串的每个字符。
  2. 对于每个字符,判断是否存在与之对应的子节点。
    • 如果存在,则将当前节点指向该子节点。
    • 如果不存在,则返回0,表示没有以该前缀开头的字符串。
  3. 在遍历完所有字符后,返回当前节点的 pass 计数,即表示所有以该前缀开头的字符串的出现次数。

示例与测试

在代码示例中,我们通过随机生成字符串数组的方式进行了多次测试,分别使用 Trie1Trie2Right (正确的实现方式)进行插入、删除、搜索和前缀匹配操作,并对结果进行了比较验证。
// 示例代码

package class08;import java.util.HashMap;// 该程序完全正确
public class Code01_TrieTree {public static class Node1 {public int pass;//记录字符经过的个数public int end;//记录字符结束的个数public Node1[] nexts;//节点数组// char tmp = 'b'  (tmp - 'a')public Node1() {pass = 0;end = 0;nexts = new Node1[26];}}public static class Trie1 {private Node1 root;public Trie1() {root = new Node1();}public void insert(String word) {if (word == null) {return;}char[] str = word.toCharArray();Node1 node = root;//头节点node.pass++;//有字符经过时int path = 0;for (int i = 0; i < str.length; i++) { // 从左往右遍历字符path = str[i] - 'a'; // 由字符,对应成走向哪条路if (node.nexts[path] == null) {node.nexts[path] = new Node1();}node = node.nexts[path];node.pass++;}node.end++;}public void delete(String word) {if (search(word) != 0) {char[] chs = word.toCharArray();Node1 node = root;node.pass--;int path = 0;for (int i = 0; i < chs.length; i++) {path = chs[i] - 'a';if (--node.nexts[path].pass == 0) {node.nexts[path] = null;return;}node = node.nexts[path];}node.end--;}}// word这个单词之前加入过几次public int search(String word) {if (word == null) {return 0;}char[] chs = word.toCharArray();Node1 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = chs[i] - 'a';if (node.nexts[index] == null) {return 0;}node = node.nexts[index];}return node.end;}// 所有加入的字符串中,有几个是以pre这个字符串作为前缀的public int prefixNumber(String pre) {if (pre == null) {return 0;}char[] chs = pre.toCharArray();Node1 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = chs[i] - 'a';if (node.nexts[index] == null) {return 0;}node = node.nexts[index];}return node.pass;}}public static class Node2 {public int pass;public int end;public HashMap<Integer, Node2> nexts;public Node2() {pass = 0;end = 0;nexts = new HashMap<>();}}public static class Trie2 {private Node2 root;public Trie2() {root = new Node2();}public void insert(String word) {if (word == null) {return;}char[] chs = word.toCharArray();Node2 node = root;node.pass++;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {node.nexts.put(index, new Node2());}node = node.nexts.get(index);node.pass++;}node.end++;}public void delete(String word) {if (search(word) != 0) {char[] chs = word.toCharArray();Node2 node = root;node.pass--;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (--node.nexts.get(index).pass == 0) {node.nexts.remove(index);return;}node = node.nexts.get(index);}node.end--;}}// word这个单词之前加入过几次public int search(String word) {if (word == null) {return 0;}char[] chs = word.toCharArray();Node2 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {return 0;}node = node.nexts.get(index);}return node.end;}// 所有加入的字符串中,有几个是以pre这个字符串作为前缀的public int prefixNumber(String pre) {if (pre == null) {return 0;}char[] chs = pre.toCharArray();Node2 node = root;int index = 0;for (int i = 0; i < chs.length; i++) {index = (int) chs[i];if (!node.nexts.containsKey(index)) {return 0;}node = node.nexts.get(index);}return node.pass;}}public static class Right {private HashMap<String, Integer> box;public Right() {box = new HashMap<>();}public void insert(String word) {if (!box.containsKey(word)) {box.put(word, 1);} else {box.put(word, box.get(word) + 1);}}public void delete(String word) {if (box.containsKey(word)) {if (box.get(word) == 1) {box.remove(word);} else {box.put(word, box.get(word) - 1);}}}public int search(String word) {if (!box.containsKey(word)) {return 0;} else {return box.get(word);}}public int prefixNumber(String pre) {int count = 0;for (String cur : box.keySet()) {if (cur.startsWith(pre)) {count += box.get(cur);}}return count;}}// for testpublic static String generateRandomString(int strLen) {char[] ans = new char[(int) (Math.random() * strLen) + 1];for (int i = 0; i < ans.length; i++) {int value = (int) (Math.random() * 6);ans[i] = (char) (97 + value);}return String.valueOf(ans);}// for testpublic static String[] generateRandomStringArray(int arrLen, int strLen) {String[] ans = new String[(int) (Math.random() * arrLen) + 1];for (int i = 0; i < ans.length; i++) {ans[i] = generateRandomString(strLen);}return ans;}public static void main(String[] args) {int arrLen = 100;int strLen = 20;int testTimes = 100000;for (int i = 0; i < testTimes; i++) {String[] arr = generateRandomStringArray(arrLen, strLen);Trie1 trie1 = new Trie1();Trie2 trie2 = new Trie2();Right right = new Right();for (int j = 0; j < arr.length; j++) {double decide = Math.random();if (decide < 0.25) {trie1.insert(arr[j]);trie2.insert(arr[j]);right.insert(arr[j]);} else if (decide < 0.5) {trie1.delete(arr[j]);trie2.delete(arr[j]);right.delete(arr[j]);} else if (decide < 0.75) {int ans1 = trie1.search(arr[j]);int ans2 = trie2.search(arr[j]);int ans3 = right.search(arr[j]);if (ans1 != ans2 || ans2 != ans3) {System.out.println("Oops!");}} else {int ans1 = trie1.prefixNumber(arr[j]);int ans2 = trie2.prefixNumber(arr[j]);int ans3 = right.prefixNumber(arr[j]);if (ans1 != ans2 || ans2 != ans3) {System.out.println("Oops!");}}}}System.out.println("finish!");}}

总结

Trie树是一种高效的字符串处理数据结构,适用于各种需要快速搜索、插入和删除字符串的场景。本篇博客介绍了Trie树的原理和实现方式,并给出了Java代码示例。希望本篇博客能够帮助读者更好地理解和应用Trie树。
以上就是对Trie树的详细介绍和应用场景的博客。希望对您有所帮助。如有任何疑问,请随时提问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/43703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis的入门级环境搭建及增删改查,详细易懂

目录 一.mybatis的简介 二.MyBatis的环境搭建 2.1 导入pom依赖 2.2 数据库文件导入连接 2.3 修改web.xml文件 2.4 安装插件 2.5 配置文件 2.5.1 mybatis.cfg.xml文件 2.5.2 generatorConfig.xml文件 2.6 最后测试生成代码 三.MyBatis的增删改查 3.1 写service类&#xff…

Linux命令200例:nc非常有用的网络工具(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

【LVS】3、LVS+Keepalived群集

为什么用它&#xff0c;为了做高可用 服务功能 1.故障自动切换 2.健康检查 3.节点服务器高可用-HA Keepalived的三个模块&#xff1a; core&#xff1a;Keepalived的核心&#xff0c;负责主进程的启动、维护&#xff1b;调用全局配置文件进行加载和解析 vrrp&#xff1a;实…

matlab使用教程(16)—图论中图的定义与修改

1.修改现有图的节点和边 此示例演示如何使用 addedge 、 rmedge 、 addnode 、 rmnode 、 findedge 、 findnode 及 subgraph 函数访问和修改 graph 或 digraph 对象中的节点和/或边。 1.1 添加节点 创建一个包含四个节点和四条边的图。s 和 t 中的对应元素用于指定每条…

使用 MBean 和 日志查看 Tomcat 线程池核心属性数据

文章目录 CustomTomcatThreadPoolMBeanCustomTomcatThreadPool CustomTomcatThreadPoolMBean com.qww.config;public interface CustomTomcatThreadPoolMBean {String getStatus(); }CustomTomcatThreadPool package com.qww.config;import com.alibaba.fastjson.JSON; impor…

三本书与三场发布会,和鲸社区重新定义编程类书籍从阅读到实践新体验

当 AI 开发者社区配备 AI 基础设施开发平台工具时&#xff0c;它还能做什么&#xff1f; 答案是&#xff1a;过去半年&#xff0c;和鲸社区凭借在气象、医学、社科等垂直领域的长期积累以及多方伙伴的支持&#xff0c;联合举办了三场新书发布会——从 Python 到 R 语言 、从气…

Midjourney Prompt 提示词速查表 v5.2

Midjourney 最新的版本更新正不断推出令人兴奋的新功能。这虽然不断扩展了我们的AI绘图工具箱&#xff0c;但有时也会让我们难以掌握所有实际可以使用的功能和参数。 针对此问题, 小编整理了 "Midjourney Prompt 提示词速查表"&#xff0c;这是一个非常方便的 Midjo…

Java“牵手“拼多多商品详情页面数据获取方法,拼多多API实现批量商品数据抓取示例

拼多多商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取拼多多商品详情数据&#xff0c;您可以通过开放平台的接口或者直接访问拼多多商城的网页来获取商品详情信息。以下是两种常用方法的介绍&#xff1a;…

Linux:shell脚本数组和脚本免交互

目录 一、shell数组的定义 二、定义数组的方式 &#xff08;1&#xff09;数组名(value1 value2 value3 value4 ...) &#xff08;2&#xff09;获取数组的长度 &#xff08;3&#xff09;获取数组下标对应的值 &#xff08;4&#xff09;数组的遍历 &#xff08;5&#x…

qsort函数详解

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解qsort函数&#xff0c;如果你觉得我写的不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 文章目录 一. qsort函数参数详解1.数组首元素地址base2.数组的元素个数num和元素所占内存空间大小w…

ThreeJS——在3D地球上标记中国地图板块

Threejs3D地球标记中国地图位置 先看效果 地球预览视频效果 用到的库 TweenJS (动画库)用来做相机转场的动画Jquery(这里只用到一个 each 循环方法&#xff0c;可以使用 js 去写)ThreeJS (3D 地球制作)100000.json(全国城市经纬度)d3.v6.js用来设置平面转3D效果(本来考虑做成…

深入解析IDS/IPS与SSL/TLS和网络安全

目录 防火墙 IDS IPS DMZ VPN VPS SSL/TLS 动态IP 静态IP 防火墙 防火墙是一种网络安全设备&#xff0c;用于监控和控制网络流量&#xff0c;保护网络免受未经授权的访问、恶意攻击和威胁。防火墙可以基于规则进行数据包过滤&#xff0c;允许或阻止特定类型的流量通过…

Lead-Lag控制器形式

对于Lead-Lag&#xff08;超前—滞后&#xff09;&#xff0c;有的地方叫做控制器 Controller&#xff0c;有的地方叫补偿器 Compensator&#xff0c;有的地方叫滤波器 Filter&#xff0c;都是一个东西。 Lead-Lag也有几种不同的形式&#xff0c;一种是 G c ( s ) 1 a T s 1…

QT设置widget背景图片

首先说方法&#xff0c;在给widget或者frame或者其他任何类型的控件添加背景图时&#xff0c;在样式表中加入如下代码&#xff0c;指定某个控件&#xff0c;设置其背景。 类名 # 控件名 { 填充方式&#xff1a;图片路径 } 例如&#xff1a; QWidget#Widget {border-image: url…

无涯教程-TensorFlow - 优化器

Optimizers是扩展类&#xff0c;其中包括用于训练特定模型的附加信息&#xff0c;Optimizers类使用给定的参数初始化&#xff0c;用于提高速度和性能&#xff0c;以训练特定模型。 TensorFlow的基本Optimizers是- tf.train.Optimizer 此类在tensorflow/python/training/opti…

C语言:深度学习知识储备

目录 数据类型 每种类型的大小是多少呢&#xff1f; 变量 变量的命名&#xff1a; 变量的分类&#xff1a; 变量的作用域和生命周期 作用域&#xff1a; 生命周期&#xff1a; 常量 字符串转义字符注释 字符串&#xff1a; 转义字符 操作符&#xff1a; 算术操作符…

图神经网络 day2 图的分类

图神经网络基础算法 1 GCN2 GraphSAGE2.1 采样&#xff1a;采样固定长度的邻居2.2 聚合2.3 GraphSAGE_minibatch2.4 GraphSAGE_embedding 3 GAT4. 图网络的分类4.1 递归图神经网络 RGNN4.2 图卷积神经网络GCN4.3 图注意力网络 GAT4.4 图自动编码 GAE4.5 图时空网络 GSTN4.6 图生…

typeScript 接口和类

工具&#xff1a; PlayGround 接口 接口用来定义对象的结构和类型&#xff0c;描述对象应该具有哪些属性和方法。 它仅用于声明&#xff0c;而不是实现&#xff1b; 这对于编写可重用的代码非常有用。它可用于&#xff1a; 关键字是interface&#xff0c; 注意&#xff1a;它…

OSPF在广播类型的网络拓扑中DR和BDR的选举

指定路由器&#xff08;DR&#xff09;&#xff1a; 一个网段上的其他路由器都和指定路由器&#xff08;DR&#xff09;构成邻接关系&#xff0c;而不是它们互相之间构成邻接关系。 备份指定路由器&#xff08;BDR&#xff09;&#xff1a; 当DR出现问题&#xff0c;由BDR接…

redis事务对比Lua脚本区别是什么

redis官方对于lua脚本的解释&#xff1a;Redis使用同一个Lua解释器来执行所有命令&#xff0c;同时&#xff0c;Redis保证以一种原子性的方式来执行脚本&#xff1a;当lua脚本在执行的时候&#xff0c;不会有其他脚本和命令同时执行&#xff0c;这种语义类似于 MULTI/EXEC。从别…