Java实现字典树单词插入、查找以及删除

news2025/1/12 16:17:08

文章目录

    • 前言
    • 题目
    • 思路
    • 代码实现
      • 测试用例
        • 结果输出
    • 结语

前言

字典树又称为前缀树或Trie树,是处理字符串常见的数据结构。

字典树是一种树形结构,优点是利用字符串的公共前缀来节约存储空间,比如插入"abc"、“abcd”、
“abd”、“bc”、"bcd"之后,字典树结构如下【图中结点表红,表示有单词以此结点结尾】:
字典树

字典树基本性质如下:

  • 根节点没有字符路径。除根节点外,每一个节点都被一个字符路径找到。
  • 从根节点出发到任何一个节点,如果将沿途经过的字符连接起来,一定为某个加入过
    的字符串的前缀。
  • 每个节点向下所有的字符路径上的字符都不同。

题目

字典树又称为前缀树或 Trie 树,是处理字符串常见的数据结构。假设组成所有单词的字符
仅是“a”~“z”,请实现字典树结构,并包含以下四个主要功能。

  • void insert(String word):添加 word,可重复添加。
  • void delete(String word):删除 word,如果 word 添加过多次,仅删除一个。
  • boolean search(String word):查询 word 是否在字典树中。
  • int prefixNumber(String pre):返回以字符串 pre 为前缀的单词数量。

思路

以在字典树中搜索是否添加过单词为例:

  1. 从根结点开始搜索。

  2. 取得要查找单词的第一个字母,并根据该字母选择对应的字符路径向下继续搜索。

  3. 字符路径指向的第二层结点上,根据第二个字母选择对应的字符路径向下继续搜索。

  4. 一直向下搜索,如果单词搜索完后,找到的最后一个结点是一个终止结点,比如上图中的实心结点,说明字典树中含有这个单词,如果找到的最后一个结点不是一个终止结点,说明单词不是字典树中添加过的单词。如果单词没搜索完,但是已经没有后续的结点了,也说明单词不是字典树中添加过的单词。

插入流程与此类似;

代码实现

    private static class TireNode {
        /**
         * 表示有多少单词共用这个结点
         */
        public int path;

        /**
         * 表示有多少个单词以这个结点结尾
         */
        public int end;

        /*
         * 每个结点路径上支持存储a-z共26个字母
         */
        public TireNode[] nexts;

        public TireNode() {
            path = 0;
            end = 0;
            nexts = new TireNode[26];
        }

    }


    public static class Trie {
        private TireNode root;

        public Trie() {
            root = new TireNode();
        }

        /**
         * 插入单词
         *
         * @param word
         */
        public void insert(String word) {
            if (word == null) {
                return;
            }
            char[] array = word.toCharArray();
            //先移动到头结点处
            TireNode node = root;
            int index = 0;
            for (int i = 0; i < array.length; i++) {
                //计算得到字母ASCII对应的偏移量index 0 - 25
                index = array[i] - 'a';
                if (node.nexts[index] == null) {
                    //如果没有生成路径,则创建路径下一结点
                    node.nexts[index] = new TireNode();
                }
                //移动到下一结点,共用路径+1
                node = node.nexts[index];
                node.path++;
            }
            //最终结点对应end+1
            node.end++;

        }

        /**
         * 查找单词
         *
         * @param word
         * @return
         */
        public int search(String word) {
            if (word == null) {
                return 0;
            }
            char[] chars = word.toCharArray();
            TireNode node = root;
            int index = 0;
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                if (node.nexts[index] == null) {
                    //表示当前路径上没有存储此字母,直接返回0
                    return 0;
                }
                node = node.nexts[index];
            }
            //如果最终结点end>0,表示单词存在
            return node.end;
        }


        /**
         * 删除单词
         *
         * @param word
         */
        public void delete(String word) {
            if (search(word) == 0) {
                return;
            }
            char[] chars = word.toCharArray();
            TireNode node = root;
            int index = 0;
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                //此单词路径path需要--,如果为0表示不需要后续结点,直接置null
                if (--node.nexts[index].path == 0) {
                    node.nexts[index] = null;
                    return;
                }
                node = node.nexts[index];
            }
            //最后结点对应end数量-1
            node.end--;
        }

        /**
         * 查找字典树中存在多少单词以pre为前缀
         *
         * @param pre
         * @return
         */
        public int prefixNumber(String pre) {
            if (pre == null) {
                return 0;
            }
            char[] chars = pre.toCharArray();
            TireNode node = root;
            int index = 0;
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.path;
        }

    }

测试用例

 public static void main(String[] args) {
        Trie trie = new Trie();
        trie.insert("abc");
        trie.insert("abd");
        trie.insert("abd");
        trie.insert("bcd");
        trie.insert("bcdef");

        int bcdNum = trie.prefixNumber("bcd");
        System.out.println(bcdNum);
        int search1 = trie.search("ab");
        System.out.println(search1);
        int search2 = trie.search("abd");
        System.out.println(search2);
        trie.delete("abc");
        int search3 = trie.search("abc");
        System.out.println(search3);
    }

结果输出

0
2
0

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

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

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

相关文章

JTAG 和 SWD 接口定义

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 JTAG引脚分布接…

js的事件循环机制(详解)

答题思路&#xff1a; ● 首先基本定义&#xff0c;宏任务和微任务 ● 事件循环机制执行顺序 ● async / await 执行顺序 一、什么是事件循环 事件循环机制就是一种同步编程模型&#xff0c;用于异步处理操作。当代码中遇到需要等待一部操作结果的语句时&#xff0c;js引擎不会…

如何区分接口测试和功能测试

接口测试和功能测试的区别&#xff1a; 2023最新Jmeter接口测试从入门到精通&#xff08;全套项目实战教程&#xff09; 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者…

5.2.tensorRT基础(2)-使用onnx解析器来读取onnx文件(源码编译)

目录 前言1. ONNX解析器2. libnvonnxparser.so3. 源代码编译4. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 t…

5.3.tensorRT基础(2)-从下载onnx-tensorrt到配置好并运行起来

目录 前言1. ONNX解析器更新总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 基础-从下载 onnx-tensorrt 到配置好…

js基础算法05--查找重复的字符

开始之前 了解什么是hash table &#xff08;哈希表&#xff09; for 循环中break 和 return的区别 哈希表的方式解 第一种方式&#xff0c;用哈希表。循环俩次&#xff0c;第一次储存当前字符出现的频率&#xff0c;没出现一次&#xff0c;对象key的评率value就加一。然后第…

vue3+Luckysheet实现表格的在线预览编辑(electron可用)

前言&#xff1a; 整理中 官方资料&#xff1a; 1、github 项目地址https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/ 2、xlsx vue3 json数据导出excel_vue3导出excel_羊…

Zookeeper的基本概念以及安装

Zookeeper简介 Zookeeper是一个分布式的(多台机器同时干一件事情),开源的分布式应用程序协调服务,是Google公司Chubby产品,是Hadoop和Base重要的组件,.它是一个分布式应用程序提供一致性的服务的软件,提供的功能包括:配置服务,域名服务,分布式同步,组服务等 Zookeeper目…

Python生成中文词云图

词云&#xff08;word cloud&#xff09;&#xff0c;也称标签云&#xff0c;主要用于展示文本数据。把每个单词按照其重要性进行展示&#xff0c;主要通过字体和颜色进行区分。Python提供了wordcloud库&#xff0c;可以很容易实现词云图。“词云”看起来比较美观易理解&#x…

Python学习(十六)柱状图

zdaPython学习&#xff08;十四&#xff09;折线图开发_yikuaidabin的博客-CSDN博客 案例数据资源 ↑ """演示基础柱状图的开发 """ from pyecharts.charts import Bar from pyecharts.options import LabelOpts # 使用Bar构建基础柱状图 bar …

视频对比工具(基于python+ffmpeg+airtest实现视频抽帧比较工具)

VideoDiff&#xff1a;基于ffmpeg&#xff0c;实现视频抽帧比较工具 使用场景&#xff1a;在视频渲染模块发生迭代&#xff0c;快速回归测试其产出的视频是否存在问题&#xff0c;从而节省人工回归成本 源码地址&#xff1a;https://github.com/jiangliuer32/VideoDiff 原理图…

什么是分布式操作系统?我们为什么需要分布式操作系统?

分布式操作系统是一种特殊的操作系统&#xff0c;本质上属于多机操作系统&#xff0c;是传统单机操作系统的发展和延伸。它是将一个计算机系统划分为多个独立的计算单元(或者也可称为节点)&#xff0c;这些节点被部署到每台计算机上&#xff0c;然后被网络连接起来&#xff0c;…

【计算机网络 01】说在前面 信息服务 因特网 ISP RFC技术文档 边缘与核心 交换方式 定义与分类 网络性能指标 计算机网络体系结构 章节小结

第一章--概述 说在前面1.1 计算机网络 信息时代作用1.2 因特网概述1.3 三种交换方式1.4 计算机网络 定义与分类1.5 计算机网络的性能指标1.6 计算机网络体系结构1 常见的计算机网络体系结构2 计算机网络体系结构分层的必要性3 计算机网络体系结构分层思想举例4 计算机网络体系结…

红包雨架构的设计汇总

一 微服务总体架构 1.1 微服务总体架构 1.2 红包雨的流程 1.3 发红包的内容 1.3.1 概述流程 1.发红包-》抢红包。 1.所有人签到的金额之和等于红包总金额。2.每个人至少抢到一分钱&#xff1b;3.保证所有人抢到金额的几率相等。 1.3.2 拆分红包通用流程算法 其中拆红包最…

详解分类指标Precision,Recall,F1-Score

文章目录 1. Precision&#xff08;精度&#xff09;2. Recall&#xff08;召回率&#xff09;3. F1-Score4. Accuracy&#xff08;准确率&#xff09;5. P-R 曲线6. TPR、FPR6.1 TPR&#xff08;真正率&#xff09;6.2 FPR&#xff08;假正率&#xff09; 7. ROC曲线8. AUC曲线…

【SpringBoot项目】Tomcat started on port(s): 8080 (http) with context path ‘‘

运行程序后出现下面的错误&#xff0c;并且在postman中无法获取到数据 在idea中的错误显示的如下 本人的原因是忘记在Controller中忘记写&#xff01;&#xff01;&#xff01;&#xff01; RestController 如果你不是以下原因可以参考下面的文章&#xff1a; Initializing S…

SSTI无过滤

解题步骤 打开环境后就只有一段文字&#xff0c;说密码错误&#xff0c;来回看了源码&#xff0c;抓包都没有什么提示&#xff0c;并且也没有有任何的传参显示 最后想来想去&#xff0c;终于灵机一动&#xff0c;这段文字就是在提示我们&#xff0c;可传的参数为password 所以…

[JAVAee]多线程入门介绍及其创建与基础知识

目录 1.进程 2.线程 3.进程与线程的区别与联系 4.为什么会有线程? 5.创建第一个多线程程序 方法一:继承Theard类 方法二:实现Runnable接口 方法三:匿名内部类创建Thread子类对象 方法四:匿名内部类创建 Runnable 子类对象 方法五(推荐方法):lambda 表达式创建 Runna…

03、怎么理解TPS、QPS、RT、吞吐量?

通常我们都从两个层面定义性能场景的需求指标&#xff1a;业务指标和技术指标。这两个层面需要有映射关系&#xff0c;技术指标不能脱离业务指标。一旦脱离&#xff0c;你会发现你能回答”一个系统在多少响应时间之下能支持多少 TPS“这样的问题&#xff0c;但是回答不了”业务…

开放的安全影响:Elastic AI Assistant

作者&#xff1a;Dain Perkins 在过去的几年里&#xff0c;我们一直在讨论开放和透明的安全方法的好处&#xff0c;即向公众提供对我们的检测和预防功能、代码、文档等详细信息的访问&#xff0c;这将增强我们能够为客户提供的安全功能。 在本博客中&#xff0c;我们将探讨我们…