算法 - 前缀树

2023-11-20

目录

一、前缀树含义

二、代码实现

(一)前缀树实现

方式一:

 方式二:

(二)暴力实现


一、前缀树含义

前缀树:把一个“最小”单位的数据看成一个节点到另一个节点的路径,每个节点有两个属性,一个是所有数据经过这个节点的次数pass,一个是这个节点作为结束位置的次数end。

例如,一个数组["abc","abd","ab","kst"],看它怎么用前缀树表示:

(1)首先有一个节点,指向null

(2)看"abc",怎么生成前缀树

a.首先是a,第一个节点没有a路径,先生成a路径,到下一个节点,经过了第一个节点,所以第一个节点的p++,但是第一个节点不是终止位置,所以e仍=0;

而a路径指向的那个节点,因为也到达了它,所以它的p++,它也不是终止,因为它还要往后生成b路径,所以它的e=0

b.然后是b,

 

 c.然后是c,c是"abc"的终止,所以e=1

 (3)"abd" "ab"如下:

二、代码实现

比如有一个字符串数组,里面所有的数据都是26个字母组成的值,那么这个数组可以用纯暴力或者前缀树实现。

(一)前缀树实现

方式一:

先有一个节点类,这个类有三个属性,一个是pass,一个是end,还有一个是指针,即指向下一个几点的指针,因为有26个字符,所以一个节点可以往外有26个指针:

public class Node{
        private int pass;
        private int end;
        private Node[] next;

        public Node(){
            pass = 0;
            end = 0;
            next = new Node[26]; //每个节点都有26个长度,26个路径,它的下一个路径可以是26中任意一个
        }
    }

 然后再生成前缀树,提供插入数据、删除、查找、查找前缀多少方法:

public class Tire{

        private Node root;

        public Tire(){
            root = new Node();
        }
        //加入数据,比如往字符串数据中加入一个串“hji”
        public void insert(String str){
            if(str == null){
                return;
            }
            char[] chars = str.toCharArray();
            Node node = root;
            node.pass++;
            for(int i=0;i<chars.length;i++){
                //当前节点是否有这个char的路径,比如节点是不是有h这个路径
                int path = chars[i] - 'a'; //i代表表的字符,比如h到a的ascii

                if(node.next[path] == null){
                    node.next[path] = new Node();
                }
                node = node.next[path];
                node.pass++;
            }
            node.end++;
        }

        //查找数据加了几次
        public int search(String str){
            if(str == null){
                return 0;
            }
            Node node = root;
            int path = 0;
            char[] chars = str.toCharArray();
            for (int i=0;i<chars.length;i++){
                path = chars[i] - 'a';
                if(node.next[path] != null){
                    node = node.next[path];
                }else{
                    return 0;
                }
            }
            return node.end;
        }

        //删除数据
        public void delete(String str){
            if(search(str) == 0){
                return;
            }
            Node node = root;
            node.pass--;
            int path = 0;
            char[] chars = str.toCharArray();
            for(int i=0;i<chars.length;i++){
                path = chars[i] - 'a';
                if(--node.next[path].pass == 0){
                    node.next[path] = null;
                    return;
                }
                node = node.next[path];
            }
            node.end--;
        }

        //加入的字符串中,多少是以str做前缀的
        public int preNum(String pre){
            if(pre == null){
                return 0;
            }
            Node node = root;
            char[] chars = pre.toCharArray();
            int path = 0;
            for (int i=0;i<chars.length;i++){
                path = chars[i] - 'a';
                if(node.next[path] == null){
                    return 0 ;
                }
                node = node.next[path];
            }
            return node.pass;
        }
    }

 方式二:

方式一只限制26个字符,如果字符出现“12fddg”这种就不够用了,所以可以改善一下节点类,使指向下个节点的指针个数不受“只能有26个”这个限制:

public class Node{
		private int pass;
		private int end;
		private HashMap<Character,Node> hashMap;
		public Node(){
			pass = 0;
			end = 0;
			hashMap = new HashMap<>();
		}
	}

那么前缀树相应改变,其实同方式一实现一样:

public class TireTree{
		private Node root;

		public TireTree(){
			root = new Node();
		}

		//插入
		public void insert(String str){
			if(StringUtils.isBlank(str)){
				return;
			}
			Node node = root;
			char[] chars = str.toCharArray();
			for(int i=0;i<chars.length;i++){
				if(node.hashMap.get(chars[i]) == null){
					node.hashMap.put(chars[i],new Node());
				}
				node = node.hashMap.get(chars[i]);
				node.pass++;
			}
			node.end++;
		}

		//查找存在几个
		public int search(String str){
			if(str == null){
				return 0;
			}
			char[] chars = str.toCharArray();
			Node node = root;
			for(int i=0;i<chars.length;i++){
				if(node == null){
					return 0;
				}
				node = node.hashMap.get(chars[i]);
			}
			return node.pass;
		}

		//删除
		public void delete(String str){
			if(search(str) == 0){
				return;
			}
			char[] chars = str.toCharArray();
			Node node = root;
			node.pass--;
			for(int i=0;i<chars.length;i++){
				if(--node.pass == 0){
					node.hashMap.put(chars[i],null);
					return;
				}
				node = node.hashMap.get(chars[i]);
			}
			node.end--;
		}

		//多少字符串以pre为前缀
		public int preNum(String pre){
			if(pre == null){
				return 0;
			}
			Node node = root;
			char[] chars = pre.toCharArray();
			for(int i=0;i<chars.length;i++){
				if(node.hashMap.get(chars[i]) == null){
					return 0;
				}
				node = node.hashMap.get(chars[i]);
			}
			return node.pass;
		}
	}

时间复杂度:

可以发现,不论是增删查哪个方法,都只和插入字符多少有关。比如插入,从树来看,就是走了str字符个数的路程。查找前缀是一样的,走了pre字符个数的路程。

(二)暴力实现

我们可以用数组实现:

public class Violence{
        private ArrayList arrayList;
        public Violence(){
            arrayList = new ArrayList();
        }

        public void insert(String str){
            if(str == null){
                return;
            }
            arrayList.add(str);
        }

        public int search(String str){
            if(str == null){
                return 0;
            }
            int num = 0;
            for (int i=0;i<arrayList.size();i++){
                if(arrayList.get(i) == str){
                    num++;
                }
            }
            return num;
        }

        public void delete(String str){
            if(search(str) == 0){
                return;
            }
            arrayList.remove(str);
        }

        public int preNum(String str){
            if(str == null){
                return 0;
            }
            int num = 0;
            for(int i=0;i<arrayList.size();i++){
                if(((String)arrayList.get(i)).indexOf(str) == 0){
                    num++;
                }
            }
            return num;
        }
    }

时间复杂度:虽然ArrayList的add方法或者remove方法,时间复杂度分别是O(1)和O(N),但是对于查找前缀方法,需要循环整个array数组,查找是否是pre开头,不如前缀树方法时间复杂度好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

算法 - 前缀树 的相关文章

  • 在java中轮询Http服务器(重复发送http get请求)

    当对其进行 REST 调用时 我的 Web 服务器会发送一些信息 我想不断轮询该服务器 间隔5秒后重复发送HTTP GET请求 以检查返回的信息是否有任何变化 做到这一点最有效的方法是什么 您能提供一些代码示例吗 请注意 我只想开发客户端代
  • 按下按钮并在java中的新窗口中打开文件

    我创建了一个 JFrame 并放置了一个文本字段和按钮 在文本字段中我放置了从文本文件读取的名称 我知道我想单击按钮并打开一个已知窗口 我想在其中放置名称 其他信息来自同一个文件 这是我的代码 这是我的主框架 package Fronten
  • 使用 Java 的 Apache Http 摘要身份验证

    我目前正在开发一个 Java 项目 但无法使 http 摘要身份验证正常工作 我尝试使用 Apache 网站 但没有帮助 我有一个需要 HTTP 摘要身份验证的网站 DefaultHttpClient httpclient new Defa
  • java.lang.ClassNotFoundException:javax.mail.MessagingException

    我想使用 eclipse 将电子邮件从我的 gmail 帐户发送到另一个邮件帐户 我使用 apache tomcat 7 0 34 作为我的 Web 服务器 并使用端口 8080 作为 apache 服务器 HTTP 1 1 并使用 JRE
  • 垃圾收集器如何在幕后工作来收集死对象?

    我正在阅读有关垃圾收集的内容 众所周知 垃圾收集会收集死亡对象并回收内存 我的问题是 Collector 如何知道任何对象已死亡 它使用什么数据结构来跟踪活动对象 我正在研究这个问题 我发现GC实际上会跟踪活动对象 并标记它们 每个未标记的
  • eclipse行号状态行贡献项是如何实现的?

    我需要更新状态行编辑器特定的信息 我已经有了自己的实现 但我想看看 eclipse 贡献项是如何实现的 它显示状态行中的行号 列位置 谁能指点一下 哪里可以找到源代码 提前致谢 亚历克斯 G 我一直在研究它 它非常复杂 我不确定我是否了解完
  • java inputstream 打印控制台内容

    sock new Socket www google com 80 out new BufferedOutputStream sock getOutputStream in new BufferedInputStream sock getI
  • 如何在单个查询中搜索 RealmObject 的 RealmList 字段

    假设我有一堂课 public class Company extends RealmObject private String companyId private RealmList
  • 在 Java 中如何找出哪个对象打开了文件?

    我需要找出答案哪个对象在我的 Java 应用程序中打开了一个文件 这是为了调试 因此欢迎使用工具或实用程序 如果发现哪个对象太具体了 这class也会很有帮助 这可能很棘手 您可以从使用分析器开始 例如VisualVM http visua
  • Android 无法解析日期异常

    当尝试解析发送到我的 Android 客户端的日期字符串时 我得到一个无法解析的日期 这是例外 java text ParseException 无法解析的日期 2018 09 18T00 00 00Z 位于 偏移量 19 在 java t
  • 如何在java中将日期格式从YYMMDD更改为YYYY-MM-DD? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我从机器可读代码中获取日期格式为 YYMMDD 如何将其更改为 YYYY MM DD 例如我收到 871223 YYMMDD 我想把它改成
  • 从jar中获取资源

    我有包含文件的 jar myJar res endingRule txt myJar wordcalculator merger Marge class 在 Marge java 中我有代码 private static final Str
  • 提高 PostgreSQL 1 亿数据左连接查询性能

    我在用Postgresql 9 2 version Windows 7 64 bit RAM 6GB 这是一个Java企业项目 我必须在我的页面中显示订单相关信息 有三个表通过左连接连接在一起 Tables TV HD 389772 行 T
  • 在 Java 中获取并存储子进程的输出

    我正在做一些需要我开始子处理 命令提示符 并在其上执行一些命令的事情 我需要从子进程获取输出并将其存储在文件或字符串中 这是我到目前为止所做的 但它不起作用 public static void main String args try R
  • hibernate 6.0.2.Final 和 spring boot 2.7.0 的entityManagerFactory bean 未配置问题

    所以最近我想升级我的 Spring Boot 项目项目的一些依赖项 特别是这些组件 雅加达 EE 9 弹簧靴2 7 休眠 6 0 2 Final 完成此操作后 所有更新和代码折射 更新将 javax 导入到 jakarta 以及一些 hib
  • 使用 HtmlUnit 定位弹出窗口

    我正在构建一个登录网站并抓取一些数据的程序 登录表单是一个弹出窗口 所以我需要访问这个www betexplorer com网站 在页面的右上角有一个登录链接 写着 登录 我单击该链接 然后出现登录弹出表单 我能够找到顶部的登录链接 但找不
  • Hibernate 本机查询 - char(3) 列

    我在 Oracle 中有一个表 其中列 SC CUR CODE 是 CHAR 3 当我做 Query q2 em createNativeQuery select sc cur code sc amount from sector cost
  • Java 正则表达式中的逻辑 AND

    是否可以在 Java Regex 中实现逻辑 AND 如果答案是肯定的 那么如何实现呢 正则表达式中的逻辑 AND 由一系列堆叠的先行断言组成 例如 foo bar glarch 将匹配包含所有三个 foo bar 和 glarch 的任何
  • 子类构造函数(JAVA)中的重写函数[重复]

    这个问题在这里已经有答案了 为什么在派生类构造函数中调用超类构造函数时 id 0 当创建子对象时 什么时候在堆中为该对象分配内存 在基类构造函数运行之后还是之前 class Parent int id 10 Parent meth void
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于

随机推荐

  • crmeb重新安装_手动安装教程 · CRMEB 知识付费版 帮助文档 · 看云

    手动安装 1 创建数据库 倒入数据库文件 数据库文件目录 public install zhishifufei sql 2 修改数据库连接文件 配置文件路径 application database php 数据库类型 type gt my
  • vagrant启动openshift

    1 Install Vagrant 2 Install VirtualBox Ex yum install VirtualBox from the RPM Fusion repository 3 In your bashrc file or
  • 元胞自动机算法汇总含matlab代码_数学建模(十三)

    元胞自动机理论 许多复杂的问题都可以通过元胞自动机来建立模型 元胞自动机实质上是定义在一个具有离散 有限状态的元胞组成的元胞空间上 并按照一定的局部规则 在离散的时间维度上演化的动力学系统 元胞又可称为单元 细胞 是元胞自动机的最基本的组成
  • 【hortonworks/registry】registry 如何添加新的类型 支持 json

    1 概述 hortonworks registry 支持json 但是要自己扩展 有相关接口 支持基本类型 支持自定义对象类型 支持集合类型 map array null 支持嵌套结构 registry支持的数据类型有好几种 其中有Avro
  • STM32F103C8T6+PWM+DMA驱动 WS2812灯带

    STM32 PWM DMA驱动 WS2812灯带 文章目录 1 理论 2代码 理论 1 WS2812参考数据手册 https wenku baidu com view 0925958fba68a98271fe910ef12d2af90342
  • 基于Matlab卡尔曼滤波的IMU和GPS组合导航数据融合(附上源码+数据)

    本文介绍了如何使用Matlab实现惯性测量单元 IMU 和全球定位系统 GPS 组合导航数据融合的卡尔曼滤波算法 通过将IMU和GPS的测量数据进行融合 可以提高导航系统的精度和鲁棒性 我们将详细介绍卡尔曼滤波的原理和实现步骤 并给出源码
  • SpringBoot使用Pio-tl动态填写合同(文档)

    poi tl poi template language 是Word模板引擎 使用Word模板和数据创建很棒的Word文档 poi tl官方网址 项目中有需求需要动态填充交易合同 因此想到了使用poi tl技术来实现 一 引入依赖
  • Keil5无法进入debug(卡死在启动文件)

    Keil5无法进入debug 卡死在启动文件 出现的情况 运行一直卡死在启动文件 例如startup stm32f103xe s 而主程序的箭头也只有一个 两个箭头的运行行在启动文件 debug一直无法运行 解决办法 你在程序中使用了pri
  • Qml与C++交互4:C++信号与Qml的槽函数的连接

    Qml与C 交互4 C 信号与Qml的槽函数的连接 使用场景 整体思路 1 建立C 信号 2 C 实例注册到qml 3 qml中建立槽函数 Connections 类型 建立槽函数 运行结果 使用属性 更多资讯 知识 微信公众号搜索 上官宏
  • OpenCV项目编译错误

    编译遇到如下错误 opencv 3 4 4 modules highgui src window gtk cpp 1062 error 218 No OpenGL support Library was built without Open
  • 长春地铁一号线作业

    长春一号线作业 代码如下 public class 第一次作业 public static void main String args System out println 北环城站 一匡街 胜利公园 解放大路 工农广场 卫星广场 华庆路
  • 卡尔曼及扩展卡尔曼滤波详细推导-来自DR_CAN视频

    卡尔曼及扩展卡尔曼滤波详细推导 来自DR CAN视频 见知乎https zhuanlan zhihu com p 585819291
  • Pytorch权重初始化方法——Kaiming、Xavier

    Pytorch权重初始化方法 Kaiming Xavier 结论 结论写在前 Pytorch线性层采取的默认初始化方式是Kaiming初始化 这是由我国计算机视觉领域专家何恺明提出的 我的探究主要包括 为什么采取Kaiming初始化 考察K
  • window10 设置 cmd 与 PowerShell 格式UTF-8

    win R键 输入 regedit 进入 如果进入不了就去下载 regedit cmd 接下来我们进入对应目录添加对应字符串 好了我们重启vscode运行即可 PowerShell 原CodePage数值数据 更改CodePage数值数据
  • Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一 实践 参考 前言 遮罩纹理 mask texture 是本章要介绍的最后一种纹理 它非常有用 在很多商业游戏中 都可以见到它的身影 那么什么是遮罩呢 简单来讲 遮罩允许我们
  • WIN10 系统,笔记本电脑显示 “未检测到摄像头”

    笔记本电脑无缘无故不能使用摄像头了 在打开腾讯会议的时候显示 未检测到摄像头 检测设备是否连接 打开设备管理器发现没有 照相机 这个选项 并且在狠心下载360卫士进行系统修复后和驱动检测发现不是驱动的问题之后 摄像头仍然无法使用 在尝试多种
  • 如何使用Minio进行对象存储和数据管理

    Minio是一个开源的对象存储服务器 可用于存储和管理各种类型的数据 包括图像 视频 文档等等 本文将介绍如何安装和配置Minio 使用Minio进行对象存储 以及如何利用Minio的高级功能和解决常见问题 一 简介 1 1 什么是Mini
  • 【Linux 应用】网络相关开发---ip、网关、掩码、dns、mac的获取和设置,以及dhcp动态获取

    最近开始调试Linux 的测试版 需要开发网络设置相关功能 其实这一块以前也做过 但是都忘记了 可见沉淀的重要性 1 ip 掩码设置和获取 通过int ioctl int d int request 这个函数可以获取到 其中 IP设置 SI
  • C语言算法题之二叉树的路径和

    思路 二叉树顾名思义就是一个最多有两个子节点的数据结构 如下图所示 其中像数字7和8 5和6这四个节点都叫做叶子节点 其他的节点都是叫做根节点 路径有 1 2 4 7 路径和为1 2 4 7 14 1 2 4 8 路径和为1 2 4 8 1
  • 算法 - 前缀树

    目录 一 前缀树含义 二 代码实现 一 前缀树实现 方式一 方式二 二 暴力实现 一 前缀树含义 前缀树 把一个 最小 单位的数据看成一个节点到另一个节点的路径 每个节点有两个属性 一个是所有数据经过这个节点的次数pass 一个是这个节点作