前缀树及其实现解析

news2024/9/24 18:43:32

前缀树

前缀树:又称单词查找树或键树,是一种哈希树的变种。

典型应用是用于统计和排序大量的字符串(但不仅限于字符串)

利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较。

将一组字符串数组放入前缀树中的演示

String[] str = {"abc", "bck", "abd", "ace"};

从root头节点开始,将每个字符串放入树中。

在放入第一个字符串第一个字符‘a’的时候,看头节点中有没有a的路径,如果没有,就创建;如果有,就沿着a的路径走

因此在放入字符‘b’、‘c’的时候,都创建新的路径


在放入第二个字符串的时候,依旧是从头节点开始。此时头节点没有b的路径,创建新的路径

因此在放入字符‘c’、‘k’的时候,都创建新的路径


在放入第三个字符串的时候,头节点存在a的路径,沿着a的路径向下走

在a之后的节点,存在b的路径,沿着b的路径向下走

在b之后的节点,不存在d的节点,创建新的路径

... ...

前缀树的实现解析

前缀树的节点

这里使用的是经典的用数组表示路径,因为在前缀树的相关题目中,一般会限制字符串的范围

比如这道题目限制了字符串的范围仅在小写字母的范围之中

但当字符串的返回过大,创建数组十分浪费空间

此时可以用哈希表、有序表等表示路径

key表示当前是哪一条路,value表示下一个node节点

package trietree;

public class TrieNode {
    int pass;//记录这个节点被经过多少次
    int end;//记录这个节点是多少个字符串的结尾

    public TrieNode[] nexts;//每个节点的之后的路径


    public TrieNode() {
        pass = 0;
        end = 0;
        nexts = new TrieNode[26];//先预设每个节点后面有26条路径
        //我们先设定字符串的范围仅在26个小写字母之内
        //a对应0、b对应1、c对应2 .....
        //nexts[0] == null;  表示没有a的路径
        //nexts[0] != null;  表示有a的路径
    }

}

insert()方法

如何生成前缀树

pass和end在节点上,记录字符串的记录情况

经过一个节点,就给当前节点的pass++

当字符串遍历完成,给最后一个节点的end++ 

根节点的pass表示加入了多少个字符串

根节点的end表示加入了多少个空字符串

如果加入一个空字符串,那么根节点的pass+1,end+1

insert部分代码

package trietree;

public class TrieTree {
    private TrieNode root;

    public TrieTree(){
        root = new TrieNode();
    }

    public void insert(String str) {
        if (str == null) {//加入空字符串时,头节点的pass++、end++
            root.pass++;
            root.end++;
            return;
        }

        char[] chs = str.toCharArray();//把字符串切分为char型数组
        TrieNode node = root;
        node.pass++;

        int index = 0;
        for (int i = 0; i < chs.length; i++) {
            index = chs[i] - 'a';//a对应0、b对应1、c对应2 ...
            if (node.nexts[index] == null) {//不存在对应的路径
                node.nexts[index] = new TrieNode();//创建一个路径 == 为nexts数组赋值 == 创建下一个新的节点
            }
            //如果存在对应的路径,复用节点,下一个节点的pass++
            node = node.nexts[index];//node向下
            node.pass++;//此时是下一个节点,下一个节点的pass++
        }
        node.end++;//遍历完成一个字符串之后,end++
    }
}

search()方法

查询一个字符串str加入过几次

沿着字符串str从头节点向下查找

查找到str的最后一个字符的时候,当时的node节点的end值就是str加入的次数

如果查到一半其中一个节点没有后续节点,那么说明没有加入过,直接返回0

    //查询一个字符串str加入过几次
    public int search(String str) {
        TrieNode node = root;//头节点
        char[] chs = str.toCharArray();
        int index = 0;

        for (int i = 0; i < chs.length; i++) {
            index = chs[i] - 'a';
            if (node.nexts[index] == null) {//如果查到一半其中一个节点没有后续节点,那么说明没有加入过,直接返回0
                return 0;
            }
            node = node.nexts[index];//node向下查找
        }
        return node.end;//查找到str的最后一个字符的时候,当时的node节点的end值就是str加入的次数
    }

prefixNumber()方法 

查询所有加入的字符串中,有几个是以pre为前缀的

沿着字符串pre从头节点向下查找

查找到pre的最后一个字符的时候,当时的node节点的pass值就是str加入的次数

如果查到一半其中一个节点没有后续节点,那么说明没有以pre为前缀的,直接返回0

    //查询所有加入的字符串中,有几个是以pre为前缀的
    public int prefixNumber(String pre) {
        TrieNode node = root;//头节点
        char[] chs = pre.toCharArray();
        int index = 0;

        for (int i = 0; i < chs.length; i++) {
            index = chs[i] - 'a';
            if (node.nexts[index] == null) {//如果查到一半其中一个节点没有后续节点,那么说明没有以pre为前缀的,直接返回0
                return 0;
            }
            node = node.nexts[index];//node向下查找
        }
        return node.end;//查找到pre的最后一个字符的时候,当时的node节点的pass值就是str加入的次数
    }

delete()方法

删除在前缀树中的字符串(怎么加的怎么删)

经过一个节点,就给当前节点的pass--

当字符串遍历完成,给最后一个节点的end--

注意当节点的pass值为0的时候,这个节点不存在,把整个节点及其后续节点全部标空

    public void delete(String str) {
        if(search(str) == 0){//先确认前缀树中是否加入过str,如果没有加入过,直接返回
            return;
        }

        if (str == null) {//删除空字符串时,头节点的pass--、end--
            root.pass--;
            root.end--;
            return;
        }

        char[] chs = str.toCharArray();
        TrieNode node = root;
        node.pass--;

        int index = 0;
        for (int i = 0; i < chs.length; i++) {
            index = chs[i] - 'a';
            //当节点的pass值为0的时候,这个节点不存在,把整个节点及其后续节点全部标空
            if (node.pass == 0) {
                node = null;//Java的JVM在标空为null之后,会自动释放内存
                return;
            }
            node = node.nexts[index];//node向下
            node.pass--;//下一个节点的pass--
        }
        node.end--;//遍历完成一个字符串之后,end--
    }

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

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

相关文章

Web 自动化神器 TestCafe(三)—用例编写篇

一、用例编写基本规范 1、 fixture 测试夹具 使用 TestCafe 编写测试用例&#xff0c;必须要先使用 fixture 声明一个测试夹具&#xff0c;然后在这个测试夹具下编写测试用例&#xff0c;在一个编写测试用例的 js 或 ts 文件中&#xff0c;可以声明多个测试夹具 fixture(测试…

内容营销频频出圈,这些品牌号做对了什么?

小红书拥有大量的年轻用户&#xff0c;通过运营品牌号既能降低投放成本&#xff0c;又能更好地连接消费者和品牌&#xff0c;在平台完成一站式闭环营销。 今天就借助几个成功案例&#xff0c;来分析下他们是如何搭建官方账号&#xff0c;通过内容运营吸引更多用户&#xff0c;实…

基于qemu_v8+optee 3.17平台的ca/ta Demo

1、整体集成构建 基于官方构建&#xff0c;加入自定义ca/ta后一体构建到rootfs&#xff0c;在qemu上运行 $ mkdir -p <optee-project> $ cd <optee-project> $ repo init -u https://github.com/OP-TEE/manifest.git -m ${TARGET}.xml [-b ${BRANCH}] $ repo syn…

【带头学C++】----- 八、C++面向对象编程 ---- 8.1 面向对象编程概述

目录 8.1 面向对象编程概述 8.1.1 面向对象概念&#xff08;OOP&#xff09; 8.1.2 面向过程概念 8.1 面向对象编程概述 8.1.1 面向对象概念&#xff08;OOP&#xff09; 面向对象&#xff08;Object-Oriented&#xff09;是一种编程范式&#xff0c;它将程序设计中的数据和…

ROS1创建自定义服务并使用

1.首先在功能包创建一个srv文件夹 如上图所示&#xff0c;vehicle_control是我的功能包&#xff0c;创建一个srv文件夹 2.使用touch指令创建服务文件 touch Ranging.srv3.在文件内输入服务数据 横线代表分割符&#xff0c;上面的是客户端发送的数据&#xff0c;下面是服务器…

Linux设置静态IP

Linux设置静态IP 使用ip addr查看ip&#xff0c;如下所示就是动态IP 1、什么是静态IP&#xff1f; 静态ip就是固定的ip&#xff0c;需要手动设置。静态IP地址&#xff08;又称固定IP地址&#xff09;是长期分配给一台计算机或网络设备使用的 IP 地址。一般来说&#xff0c;一…

C++的new / delete 与 C语言的malloc/realloc/calloc / free 的讲解

在C语言中我们通常会使用malloc/realloc/calloc来动态开辟的空间&#xff0c;malloc是只会开辟你提供的空间大小&#xff0c;并不会初始化内容&#xff1b;calloc不但会开辟空间&#xff0c;还会初始化&#xff1b;realloc是专门来扩容的&#xff0c;当你第一次开辟的空间不够用…

意图交易:为用户思考,而不是让用户思考

意图叙事 在前不久&#xff0c;知名加密投资机构 Paradigm 的 CTO 、研究员 Georgios Konstantopoulos 曾在推特上对现有 ChainAsset 模式的糟糕且割裂的体验进行了吐槽&#xff0c;这也道出了很多链上用户的痛点。同时他也认为现有加密基建设施需要为用户思考&#xff0c;而不…

NSGA-II求解微电网多目标优化调度(MATLAB)

一、NSGA-II简介 NSGA-Ⅱ算法是Kalyanmoy Deb等人于 2002年在 NSGA 的基础上提出的&#xff0c;它比 NSGA算法更加优越&#xff1a;它采用了快速非支配排序算法&#xff0c;计算复杂度比 NSGA 大大的降低&#xff1b;采用了拥挤度和拥挤度比较算子&#xff0c;代替了需要指定的…

flutter,uni-app开发调试ios

一、申请ios开发者账号 二、ios开发者配置 ios 开发者需要配置的地方 https://developer.apple.com/account/resources/certificates/list Certificates&#xff08;证书&#xff09;: 作用&#xff1a; 证书用于对应用程序和开发者进行身份验证&#xff0c;确保安全性和可…

文心一言 VS 讯飞星火 VS chatgpt (141)-- 算法导论12.1 1题

一、用go语言&#xff0c;对于关键字集合(1&#xff0c;4&#xff0c;5&#xff0c;10&#xff0c;16&#xff0c;17&#xff0c;21)&#xff0c;分别画出高度为 2、3、4、5和6的二又搜索树。 文心一言&#xff1a; 以下是用Go语言为给定的关键字集合(1, 4, 5, 10, 16, 17, 2…

【深度学习】如何选择神经网络的超参数

1. 神经网络的超参数分类 神经网路中的超参数主要包括: 1. 学习率 η 2. 正则化参数 λ 3. 神经网络的层数 L 4. 每一个隐层中神经元的个数 j 5. 学习的回合数Epoch 6. 小批量数据 minibatch 的大小 7. 输出神经元的编码方式 8. 代价函数的选择 9. 权重初始化的方法 …

4.4 抗锯齿

一、锯齿是怎么产生的 二、抗锯齿介绍 1.SSAA(super sample anti-aliasing) 拿4xSSAA举例子&#xff0c;假设最终屏幕输出的分辨率是800x600, 4xSSAA就会先渲染到一个分辨率1600x1200的buffer上&#xff0c;然后再直接把这个放大4倍的buffer下采样至800x600。这种做法在数学上…

卷积神经网络(AlexNet)鸟类识别

文章目录 一、前言二、前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、数据预处理1. 加载数据2. 可视化数据3. 再次检查数据4. 配置数据集 三、AlexNet (8层&#xff09;介绍四、构建AlexNet (8层&#xff09;网络模型五、…

pair和typedef

文章目录 一、pair用法1.2、pair的创建和初始化1.3、pair对象的操作1.4、(make_pair)生成新的pair对象1.5、通过tie获取pair元素值 2、typedef2.1、什么是typedef2.2、typedef用法2.2.1、对于数据类型使用例如&#xff1a;2.2.2、对于指针的使用例如2.2.3、对于结构体的使用 2.…

科技与艺术如何交织出“理想之家”?三星电视给出家电行业最优解答

作者 | 曾响铃 文 | 响铃说 理想的家&#xff0c;是什么样子? 关于这个问题&#xff0c;社交媒体上有形形色色的答案。很多人的梦中情屋是原木风、奶油色&#xff0c;点缀着绿意盎然的植物&#xff1b;还有一些人的Dream house是用全屋智能将科技感拉满&#xff0c;再配上打…

Linux 用户必备的 8 大网站

目录 前言 1、ArchWiki 2、ExplainShell 3、Crontab.guru 4、DistroWatch 5、Phoronix 6、Ubuntu Blog 7、GamingOnLinux 8、Reddit 的 /r/Linux 子论坛 我收藏了什么&#xff1f; 前言 这里列举的是我个人最喜欢的 Linux 网站&#xff0c;希望你也能喜欢。 对 Linux …

Redis报错:JedisConnectionException: Could not get a resource from the pool

1、问题描述&#xff1a; redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool 2、简要分析&#xff1a; redis.clients.util.Pool.getResource会从JedisPool实例池中返回一个可用的redis连接。分析源码可知JedisPool 继承了 r…

无需公网IP,使用内网穿透实现公网访问本地OpenWRT管理界面

文章目录 1.openWRT安装cpolar2.配置远程访问地址3.固定公网地址 简单几步实现在公网环境下远程访问openWRT web 管理界面&#xff0c;使用cpolar内网穿透创建安全隧道映射openWRT web 界面面板443端口&#xff0c;无需公网IP&#xff0c;无需设置路由器。 1.openWRT安装cpola…

C++11『lambda表达式 ‖ 线程库 ‖ 包装器』

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2022 版本 17.6.5 文章目录 &#x1f307;前言&#x1f3d9;️正文1.lambda表达式1.1.仿函数的使用1.2.lambda表达式的语法1.3.lambda表达式的使用…