【大数据管理】Java实现字典树TireTree

news2025/1/16 13:47:57

实现字典树,支持插入和删除,能够打印每一层的数据
示例数据“SJ”, “SHJ”, “SGYY”,"HGL" ,将这些数据插入前缀树,打印树,修改SHZSHHZ

解题思路

Trie树即字典树,又称单词查找树或键树,是一种树形结构,哈希树的变种。典型应用是用于统计和排序大量的字符串,所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。Trie树的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有3个基本性质:首先,根节点不包含字符,除根节点外每一个节点都只包含一个字符;其次,从根节点到某一节点路径上经过的字符连接起来,为该节点对应的字符串;最后,每个节点的所有子节点包含的字符都不相同。结点类Node的主要数据结构有:Character类型的name,表示当前结点的字符;TreeMap<Character,Node>类型的子节点列表next;布尔类型的isWordEnd表示当前结点是否为某个单词的结尾;Node类型的双亲结点parent和int类型的prefixCount表示前缀经过这个节点的字符的数量

如果要插入一个单词word,首先要将该字符串拆分为一个个字符,然后每个字符作为一个节点依次从上往下插入。初始时当前结点cur为根结点,如果cur节点的子节点们不存在该字符,就直接将该子节点插入;否则说明已存在相同前缀,将前缀数量prefixCount自增。所有字符都处理完时cur指针指向这个单词的最后一个字符节点,如果这个节点还不是表示一个单词结尾,则把isWordEnd置为true。如果要删除单词,先向下搜索到此字符串的最后一个子节点,如果字符串不存在则无需删除;如果存在, 则看是不是叶子节点, 如果不是叶子节点则直接把节点的单词标记prefixCount置为false;如果是叶子节点,则一直往上搜索到被使用过的节点就停止搜索,因为从这个结点开始的所有结点都是为了索引要删除的这个单词,所以要把这些仅为了所以该单词的结点全部删除。删除后还需要将该路径上所有结点的prefixCount自减。字典树的层序遍历采用的是广度优先搜索(BFS)的思想,使用一个队列queue,初始时将根节点root进队,如果队列不为空,就输出队头的元素;再判断节点是否有孩子,如果有就将孩子进队,将遍历过的结点出队,循环以上步骤直到队列为空。若要查询单词word是否在Trie中,按照word每个字符顺序向下搜索即可。

import java.util.*;
public class trieTree {

    class Node {
        //当前节点表示的字符
        public Character name;

        // 子节点列表 Map<子节点字符,子节点>
        public TreeMap<Character,Node> next;

        // 是否表示一个单词的结尾
        public boolean isWordEnd;

        // 前缀经过这个节点的字符的数量
        public int prefixCount;

        // 父节点
        private Node parent;

        public Node(boolean isWordEnd) {
            this(null,isWordEnd,0);
        }

        // 构造函数
        public Node(Character name, boolean isWordEnd, int prefixCount) {
            this.name = name;
            this.isWordEnd = isWordEnd;
            this.prefixCount = prefixCount;
            this.next = new TreeMap<>();
        }

        public Node(Character name, boolean isWordEnd, int prefixCount, Node parent) {
            this(name,isWordEnd,prefixCount);
            this.parent = parent;
        }
    }

    // 根节点
    private Node root;

    //字典树中单词的个数
    private int size;

    public trieTree() {
        this.root = new Node(false);
        this.size = 0;
    }


    /**
     * 添加单词word
     先将字符串拆成每个字符, 然后每个字符作为一个节点依次从上往下插入即可。 生成的树的路径结构刚好就是字符串字符的顺序。
     */
    public void add(String word){

        Node cur = this.root;
        for (char key : word.toCharArray()) {
            //cur节点的子节点们不存在该字符,则直接插入该子节点即可
            if(!cur.next.containsKey(key)){
                cur.next.put(key,new Node(key,false,1,cur));
            }else{
                //存在相同前缀, 前缀数量+1
                cur.next.get(key).prefixCount++;
            }
            // 更新指针
            cur = cur.next.get(key);
        }

        // 此时 cur指针指向一个单词的最后一个字符节点,如果这个节点还不是表示一个单词结尾,则标记它
        if (!cur.isWordEnd){
            cur.isWordEnd = true;
            this.size++;
        }
    }

    /**
     *  删除单词

     先向下搜索到此字符串的最后一个子节点。 如果字符串不存在则无需删除。 如果存在, 则看是不是
     叶子节点, 如果不是叶子节点直接把节点的单词标记位清除即可。
     如果是叶子节点, 则一直往上搜索是标记单词的节点 或者 是被使用过的节点就停止搜索(说明从该节点开始是无需删除的),
     然后从直接删除该节点下的要被删除的子节点即可。
     */
    public void remove(String word){
        Node node = getPrefixLastNode(word);
        if (node == null || !node.isWordEnd){
            System.out.println("单词不存在");
            return;
        }

        // 如果不是叶子节点直接把单词标记去掉即可
        if (!node.next.isEmpty()){
            node.isWordEnd = false;
        }else{
            // 往上找到是标记单词的 或者 被使用过的节点 就停止
            Node pre = node;    //指向需要被删除的子树的第一个节点
            node = node.parent; // 当前迭代指针
            while (node != null && !node.isWordEnd && node.prefixCount <= 1){
                pre = node;
                node = node.parent;
            }

            // 删除节点node的子节点pre.name
            if (node != null){
                node.next.remove(pre.name);
            }
        }

        // 更新 从 root -> node路径上所有节点的 prefixCount 减1
        while(node != null){
            node.prefixCount--;
            node = node.parent;
        }
    }


    /**
     *  层次遍历
     */
    public void levelOrder() {
        Queue<Node> queue = new ArrayDeque<>();
        queue.offer(this.root);

        // 上一层的最后一个节点
        Node preLayerLastNode = this.root;
        // 本层最后一个节点
        Node curLayerLastNode = this.root;

        int curLayer = 0; // 当前层数

        while(!queue.isEmpty()){
            Node tmp = queue.remove();

            if (curLayer != 0){
                System.out.print(tmp.name +"("+ tmp.prefixCount+"-" + tmp.isWordEnd + ")" + "\t");
            }

            TreeMap<Character, Node> treeMap = tmp.next;
            if (treeMap != null && !treeMap.isEmpty()){
                List<Node> arrayList = new ArrayList<>(treeMap.values());
                queue.addAll(arrayList);

                if (!arrayList.isEmpty()){
                    curLayerLastNode = arrayList.get(arrayList.size()-1);
                }
            }
            //遍历到每一层的末尾就进行换行
            if (preLayerLastNode.equals(tmp)){
                curLayer++;
                preLayerLastNode = curLayerLastNode;
                System.out.print("\n" + curLayer + "| ");
            }
        }
    }
    /**
     * 查询单词word是否在Trie中
     按照word每个字符顺序向下搜索即可
     */
    public boolean contains(String word) {
        Node node = getPrefixLastNode(word);
        return node != null && node.isWordEnd;
    }

    /**
     * 查询是否在Trie中有单词以prefix为前缀
    按照prefix每个字符顺序向下搜索即可
     */
    public boolean hasPrefix(String prefix){
        return getPrefixLastNode(prefix) != null;
    }

    /**
     *  词频统计
     *  获取前缀prefix的数量
     */
    public int getPrefixCount(String prefix){
        Node node = getPrefixLastNode(prefix);
        return node != null ? node.prefixCount : 0;
    }

    // 获取前缀表示的最后一个节点
    private Node getPrefixLastNode(String prefix){
        // 往下搜每个字符节点,能搜到结尾即代表存在并返回
        Node cur = root;
        for (char key : prefix.toCharArray()) {
            if(!cur.next.containsKey(key))
                return null;
            cur = cur.next.get(key);
        }
        return cur;
    }

    public static void main(String []args){
        trieTree trie = new trieTree();

        trie.add("SJ");
        trie.add("SHJ");
        trie.add("SGYY");
        trie.add("HGL");

        System.out.println(trie.contains("SHJ"));

        trie.levelOrder();

        trie.remove("SHJ");
        trie.add("SHHJ");

        trie.levelOrder();

    }
}

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

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

相关文章

Linux下进程控制详解

目录 一、进程创建 1.1 初识fork 1.2 函数返回值 1.3 写时拷贝技术 1.4 fork函数的使用场景 1.5 fork函数的失败原因 二、进程终止 2.1 进程退出场景 2.2 进程退出码 2.3 进程正常退出方法 2.3.1 exit函数 2.3.2 _exit函数 2.3.3 return方法 2.3.4 方法分析对比 …

【LINUX修行之路】——工具篇gcc/g++的使用和自动化构建工具make/makefile

学习范围&#xff1a;✔️LINUX ✔️ gcc/g✔️make/makefile作者 &#xff1a;蓝色学者 文章目录一、前言二、概念什么是gcc/g&#xff1f;什么是make/makefile&#xff1f;三、教程3.1gcc/g命令3.2make/makefile依赖关系依赖方法编写makefile文件四、资源一、前言 欢迎大家来…

谷粒学院——Day20【项目总结】

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

计算机组成原理实验-logisim实现自动售糖机

一.作业内容; 二.设计分析&#xff1a; 首先我们先确定输入和输出&#xff0c;根据题目的提示很明显可以看出因为每次可以投入10元或者5元硬币&#xff0c;当总钱数达到15元或者超过15元的时候&#xff0c;自动出糖&#xff0c;并且机器不找零&#xff0c;所以可以看出最大的钱…

基于 V2G 技术的电动汽车实时调度策略(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

第九层(2):STL之string类

文章目录前情回顾string类string类的本质string与char*的区别string类的特点string类的构造函数string类内的字符串追加函数string类内的字符串查找函数string类内的字符串替换函数string类内的字符串比较函数string类内的字符单个访问函数string类内的插入函数string类内的删除…

最小化最大值+拓扑排序要点+概率

今天嫖来的两道题&#xff1a; D.ScoreofaTreeD. Score of a TreeD.ScoreofaTree E.EdgeReverseE. Edge ReverseE.EdgeReverse DDD题是比较离谱的一道题&#xff0c;你在做的时候好像是dp&#xff0c;但是选择的情况太多了&#xff0c;其实对于每一个节点来说&#xff0c;除了叶…

fpga实操训练(fpga和cpu之间的配合)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 cpu和fpga之间,各有各的优势,cpu开发比较快捷,程序员比较好找;fpga对于基础运算效率高,但是找人不好找。实际产品的开发中,一般cpu负责需要接口定义和个性化定制的地方,而fp…

【Datewhale一起吃瓜 Task3】啃瓜第四章

文章目录决策树学习过程预测过程如何划分信息熵信息增益增益率基尼指数泛化能力关键&#xff1a;剪枝预剪枝后剪枝比较缺失值处理&#xff1a;样本赋权&#xff0c;权重划分决策树 决策树基于“树”结构进行决策 每个内部节点对应于某个属性上的测试每个分支对应于该属性的某个…

OpenGL ES着色器语言(GLSL ES)规范 ——下篇

文章目录前言分支和循环if、if-elseforcontinue、break、discard着色器内置变量函数函数定义规范声明webgl内置函数存储限定字constattributeuniformvarying精度限定字预处理指令总结前言 本篇接上文继续对着色器语言规范进行讲解&#xff0c;本文的内容包括&#xff1a;分支和…

Windows下JetBrains GoLand环境配置记录

闲来无事&#xff0c;go go go 这篇文章不是最简单的配置方法&#xff0c;相对简单的配置方法见文末引用。 本文记录了我遇见的一些问题以及解决方案与解释。 Go编译环境配置 首先得前往谷歌的网站下载go语言的镜像文件&#xff1a; Downloads - The Go Programming Languag…

1. 数据仓库维度建模简介

数据仓库的设计目的软件产品来源于用户的需求&#xff0c;因此&#xff0c;在深入数据仓库的设计之前&#xff0c;我们需要了解客户的痛点有哪些&#xff0c;整理如下&#xff1a;我们收集了海量的数据&#xff0c;但无法对其访问&#xff1b;我们需要以各种方式方便的对数据进…

C C++实现两矩阵相乘--模拟法

目录前言数学中两矩阵怎么相乘?C/C语言实现运行结果前言 11月左右大三找日常实习的时候&#xff0c;面试乱杀&#xff0c;但是笔试碰到了这个矩阵相乘的编程题有几次&#xff0c;可能脑瓜子晕&#xff0c;突然被绕来绕去写不出来&#xff0c;很无语&#xff0c;现在总结一下;…

CS61A 2022 fall lab01

CS61A 2022 fall lab01 文章目录CS61A 2022 fall lab01TopicsDivision, Floor Div, and ModuloFunctionsCall expressionsreturn and printControlBoolean operatorsShorting Circuiting(短路效应)If StatementsWhile LoopsError MessagesRequired QuestionsWhat Would Python …

AI算法(三)plt基础

目录 一、前言 二、各类图 2.1、折线图 2.2、散点图 2.3、点线图 2.4、下三角线 2.5、点虚线 2.6、虚点线 2.7、绘制自己的学习曲线 三、多线 四、画布 五、直方图 一、前言 plt是深度学习的常用库之一&#xff0c;很多指标结果如AUC、F1、ROC等都是通过plt来实现。本篇文章主…

【每日数据结构与算法】

这里面有 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树; 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算 法、动态规划、字符串匹配算法。 文章目录一、 基本算法思想1-1 回溯1-2 动态规划dp1-3二、 排序2-1 O(n…

【015 关键字】typedef和define的区别

一、两者区别 关键字typedefdefine&#xff08;宏&#xff09;作用不同定义&#xff08;标识符或关键字&#xff09;别名简单字符串替换执行时间不同编译过程一部分预处理过程完成作用域不同从定义到花括号“}”截至从定义到文件结尾截止 对指针操作不同 typedef int* INTPTR…

2023啦 最新无人直播小白教程!

最近看了不少up主说&#xff0c;无人直播这个东西可以做副业&#xff0c;自己手里也有一台五年的腾讯云服务器&#xff0c;一个月2t流量&#xff0c;应该是够的&#xff0c;可以玩玩。 先放出我的直播间地址看看效果&#xff1a; b站小红书&#xff08;深度sleep&#xff09;b站…

想要学会二叉树?树的概念与结构是必须要掌握的!快进来看看吧

目录 1.树的概念及结构 1.1什么是树&#xff1f; 1.2树的相关术语 1.3树的表示 2.二叉树的概念及结构 2.1二叉树的概念 2.2两种特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 2.4.1 顺序存储 2.4.2 链式存储 1.树的概念及结构 1.1 什么是树&#xff1f; 树是…

【JavaSE专栏6】Java 基本类型转换、包装类、自动装箱、自动拆箱

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…