数据结构(10)-TrieTree(字典树)的实现

【数据结构基础】树 - 前缀树(Trie Tree) 在计算机科学中,trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。Trie 这个术语来自于 retrieval。根据词源学,trie 的发明者 Edward Fredkin 把它读作/ˈtriː/ “tree”。 阅读详情

Let’s Look Look What’s TrieTree!

1.1 什么是TrieTree

TrieTree又被称为字典树/单词查找树/前缀树,常用来做大量的词频分析以及排序大量字符串,是哈希树的一种变种形式,它最大的特点就是每一个节点中只存放字符串的一个字符,而树的一条边即该字符串或者是其前缀,这样的特性使得字典树被广泛地应用于海量字符串处理程序中.

在这里插入图片描述

1.2 TrieTree的实现

要实现TrieTree,首先需要定义其基础节点类:

private class Node {
        //该节点代表的字符串前缀出现的次数
        int count;
        //以该节点为结束的字符的数量
        int wordCount;
        //记录该字符
        char value;
        //标识该节点是否代表单词结尾
        private boolean isEnd;
        //该节点的孩子节点
        Node[] child;

        private Node() {
            count = 1;
            isEnd = false;
            child = new Node[MAX_SIZE];
        }
    }

其次是TrieTree成员变量的定义:

    //每个节点下最多只能有从a-z这26个字符
    private static final int MAX_SIZE = 26;
    //字典树空根节点
    private  Node head;

再次是构造方法:

public TrieTree() {
        //初始化Trie树头节点
        Node node = new Node();
        node.count = 1;
        //根节点不包含单词字符
        node.value = '*';
        //声明子节点
        node.child = new Node[MAX_SIZE];
        head = node;
    }

1.3 重要方法

  • 1.插入字符串
    public void insert(String word) {
        if (word == null || "".equals(word)) {
            throw new IllegalArgumentException("word cant be null");
        }
        buildNode(head, word);
    }

    private void buildNode(Node node, String word) {
        char[] letters = word.toLowerCase().toCharArray();
        for (int i = 0; i < letters.length; i++) {
            //定位索引
            int index = letters[i] - 'a';
            if (node.child[index] != null) {
                //已存在该字符节点,出现次数+1
                node.child[index].count++;
            } else {
                //不存在该字符及诶单,创建
                node.child[index] = new Node();
            }
            //为相应节点赋值并让其往下走
            node.child[index].value = letters[i];
            node = node.child[index];
        }
        node.isEnd = true;
        node.wordCount++;
    }
  • 2.查找前缀出现次数
public int findPreCount(String pre) {
        if (pre == null || "".equals(pre.trim())) return -1;
        return _findPreCount(head, pre);
    }

    private int _findPreCount(Node node, String pre) {
        char[] letters = pre.toLowerCase().toCharArray();
        for (int i = 0; i < letters.length; i++) {
            int index = letters[i] - 'a';
            if (node.child[index] != null) {
                //存在该字符节点,继续往下找
                node = node.child[index];
            } else {
                //无法匹配,则返回0
                return 0;
            }
        }
        return node.count;
    }
  • 3.查找特定字符串出现次数
    public int findWordCount(String word) {
        if (word == null || "".equals(word.trim())) return -1;
        return _findWordCount(head, word);
    }

    private int _findWordCount(Node head, String word) {
        char[] chars = word.toLowerCase().toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (head.child[chars[i] - 'a'] == null) return 0;
            head = head.child[chars[i] - 'a'];
        }
        return head.isEnd ? head.wordCount : 0;
    }
  • 4.全体词频统计
public Map<String, Integer> wordCount() {
        Map<String, Integer> res = new HashMap<>();
        //使用回溯进行搜索
        backTrace(res, head, "");
        return res;
    }

    private void backTrace(Map<String, Integer> res, Node head, String oneRes) {
        if (head != null) {
            //回溯终止条件
            if (head.isEnd) {
                res.put(oneRes, head.wordCount);
            }

            //回溯搜索过程
            Node[] child = head.child;
            for (int i = 0; i < child.length; i++) {
                if (child[i] != null) {
                    //还原字符
                    char ch = (char)(i + 'a');
                    //回溯搜索
                    backTrace(res, child[i], oneRes + ch);
                }
            }
            return;
        }
    }

1.3 时间复杂度

1. 插入数据
插入数据的时间复杂度依赖于节点的遍历搜索结果,所以与单词的长度相关,为O(len)
2.查询数据
查询数据的时间复杂度依赖于需要查询的字符串长度,需要解析字符串的每个字符去搜索,所以为O(len)

1.4 经典应用

  • 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
    -----答:先使用TrieTree进行词频统计,再使用最小堆进行Top 100统计,最后求得词频最高的100个词.

  • 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
    -----答:先将这些字符串放入TrieTree中,然后使用回溯搜索获取所有的字符串即可.

1.5 完整代码

/**
 * @Auther: ARong
 * @Date: 19-9-3 上午9:57
 * @Description: 使用Java实现字典树
 **/
public class TrieTree {
    //每个节点下最多只能有从a-z这26个字符
    private static final int MAX_SIZE = 26;
    //字典树空根节点
    private  Node head;
    //字典树节点
    private class Node {
        //该节点代表的字符串前缀出现的次数
        int count;
        //以该节点为结束的字符的数量
        int wordCount;
        //记录该字符
        char value;
        //标识该节点是否代表单词结尾
        private boolean isEnd;
        //该节点的孩子节点
        Node[] child;

        private Node() {
            count = 1;
            isEnd = false;
            child = new Node[MAX_SIZE];
        }
    }

    public TrieTree() {
        //初始化Trie树头节点
        Node node = new Node();
        node.count = 1;
        //根节点不包含单词字符
        node.value = '*';
        //声明子节点
        node.child = new Node[MAX_SIZE];
        head = node;
    }

    /**
     * @auther: Arong
     * @description: 在Trie树中插入字符串
     * @param: [word]
     * @return: void
     * @date: 上午10:09 19-9-3
     */
    public void insert(String word) {
        if (word == null || "".equals(word)) {
            throw new IllegalArgumentException("word cant be null");
        }
        buildNode(head, word);
    }
    /**
     * @auther: Arong
     * @description: 用word构建字典树
     * @param: [head, word]
     * @return: void
     * @date: 上午10:15 19-9-3
     */
    private void buildNode(Node node, String word) {
        char[] letters = word.toLowerCase().toCharArray();
        for (int i = 0; i < letters.length; i++) {
            //定位索引
            int index = letters[i] - 'a';
            if (node.child[index] != null) {
                //已存在该字符节点,出现次数+1
                node.child[index].count++;
            } else {
                //不存在该字符及诶单,创建
                node.child[index] = new Node();
            }
            //为相应节点赋值并让其往下走
            node.child[index].value = letters[i];
            node = node.child[index];
        }
        node.isEnd = true;
        node.wordCount++;
    }

    /**
     * @auther: Arong
     * @description: 查找前缀出现次数
     * @param: [pre]
     * @return: data_struct.TrieTree.Node
     * @date: 上午10:30 19-9-3
     */
    public int findPreCount(String pre) {
        if (pre == null || "".equals(pre.trim())) return -1;
        return _findPreCount(head, pre);
    }

    /**
     * @auther: Arong
     * @description: 查找前缀出现次数
     * @param: [node, pre]
     * @return: void
     * @date: 上午10:31 19-9-3
     */
    private int _findPreCount(Node node, String pre) {
        char[] letters = pre.toLowerCase().toCharArray();
        for (int i = 0; i < letters.length; i++) {
            int index = letters[i] - 'a';
            if (node.child[index] != null) {
                //存在该字符节点,继续往下找
                node = node.child[index];
            } else {
                //无法匹配,则返回0
                return 0;
            }
        }
        return node.count;
    }

    /**
     * @auther: Arong
     * @description: 搜索特定单词出现的次数
     * @param: [word]
     * @return: int
     * @date: 下午8:24 19-9-5
     */
    public int findWordCount(String word) {
        if (word == null || "".equals(word.trim())) return -1;
        return _findWordCount(head, word);
    }

    private int _findWordCount(Node head, String word) {
        char[] chars = word.toLowerCase().toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (head.child[chars[i] - 'a'] == null) return 0;
            head = head.child[chars[i] - 'a'];
        }
        return head.isEnd ? head.wordCount : 0;
    }

    /**
     * @auther: Arong
     * @description: 全部词频数量统计
     * @param: []
     * @return: java.util.List<java.util.Map<java.lang.String,java.lang.Integer>>
     * @date: 下午9:33 19-9-5
     */
    public Map<String, Integer> wordCount() {
        Map<String, Integer> res = new HashMap<>();
        //使用回溯进行搜索
        backTrace(res, head, "");
        return res;
    }

    private void backTrace(Map<String, Integer> res, Node head, String oneRes) {
        if (head != null) {
            //回溯终止条件
            if (head.isEnd) {
                res.put(oneRes, head.wordCount);
            }

            //回溯搜索过程
            Node[] child = head.child;
            for (int i = 0; i < child.length; i++) {
                if (child[i] != null) {
                    //还原字符
                    char ch = (char)(i + 'a');
                    //回溯搜索
                    backTrace(res, child[i], oneRes + ch);
                }
            }
            return;
        }
    }
}
算法基础:前缀树(Trie tree、字典树) 算法成神之路:前缀树(Trie tree、字典树) 阅读详情

相关推荐

【数据结构】TrieTree(字典树、前缀树)—— C++实现

TrieTree(字典树、前缀树、单词查找树)字典树,看名字好像和字符串相关。如果你能看到这一篇文章,那么肯定学过字符串串匹配算法,即BF和KMP的模式匹配。字典树并不是来解决字符串匹配问题,而是用于以下场景:大量的单词(串),实现单词的排序功能、快速检索功能、前缀搜索功能…

Sauron7i的博客 1371

【数据结构】字典树TrieTree图文详解

问题引入 现在,我给你n个单词,然后进行q次询问,每一次询问一个单词b,问你b是否出现在n个单词中,你会如何去求呢? 暴力搜索?但是我们如果这么做的话时间复杂度一下就高上去了。大家都是成熟的ACMer了,不要再惦记着暴力的方法啦,要优雅。 你想想,问题的描述像不像查字典的操作?你平时是怎么查字典的?想想看? 如果你要在字典中查找单词“Avalon”,你是不是先找到首字母为‘A’的部分,然后再找第二个单词为‘V’的部分······最后,你可能可以找到这个单词,当然,也有可能这本词典并没有这个单词。 你想想看,

qq_49688477的博客 5万+

Linux/C/C++ TrieTree字典树实现关键词过滤

1.main.cpp #include <iostream> #include "othertrie.hpp" using namespace mytrie; int main(int argc, char *argv[]) { std::string text = "即便在世界仍深陷冷战之中的1970年,毛主席"\ "在会见美国记者斯诺时也曾表示:“中美两国总要建交的。中国和美国难道就"\ "一百年不建交啊?” 美国前总统尼克松也曾说过:“我们改善同北京的实际上的

weixin_45646368的博客 454

【图解算法】模板+变式——带你彻底搞懂字典树(Trie树)

  啥是字典树? 【字典树】(Trie Tree) 是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串)。 它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。                                                     ——百度 · 百科 so?所以到底什么是字典树?   还好,它还有其他的名字,更能表述出它的实质: 前缀树、单词查找树 &nbs

丧心病狂Loli控的博客 2万+

ADT: Trie Tree 字典树(附 Java 实现)

ADT: Trie Tree 字典树(附 Java 实现) 文章目录ADT: Trie Tree 字典树(附 Java 实现)简介参考完整示例代码正文数据结构操作接口`interface TrieTree` 接口声明具体实现`class TrieTreeImpl` 具体实现`class TrieTreeTest` 测试代码结语 简介 前段时间看到一个算法题-最长公共前缀的一种实现:使用 Trie Tree 字典树来实现。后来查阅 Trie Tree 相关的信息发现,字典树主要用于字符串排序和词频统计,公共前

超悠閒的博客 641

trie-tree:简单的字典树模型,用于查找关键字,敏感词等

字典树 简单的字典树模型,用于查找关键字,敏感词等 运行 编写测试类以测试: public class TrieTreeTest { public static void main(String[] args) { TrieTreeUtil trieTreeUtil = new TrieTreeUtil(); trieTreeUtil.add("和平"); trieTreeUtil.add("未来"); trieTreeUtil.add("守护"); trieTreeUtil.add("那咋办嘛"); //只返回结果(效率更高) System.out.println(trieTreeUtil.contains("我的愿望是世界和平~~")); Syste

TrieTree结构

TrieTree 又称为前缀树、字典树,是一种用于字符串处理的结构。给定一个字符串集合(字典),提供下面所述的操作。

Corlin Blog 669

从零到一:手把手教你用STM32F103C8T6打造个人专属开发板(附立创EDA工程与源码)

本文详细介绍了如何使用STM32F103C8T6微控制器从零开始设计和制作个人专属开发板。内容涵盖硬件选型、原理图设计、PCB布局、功能验证等全流程,并提供了立创EDA工程文件和源码,帮助开发者深入理解STM32开发板的硬件架构与设计要点。

weixin_26802933的博客 287

TrieTree字典树数据结构的原理、实现及应用

一、基本知识字典树(TrieTree),又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有3个基本性质: 根节点不包含字符,除根节点

leasonw的博客 1万+

排队论在食堂窗口规划中的应用:M/M/S模型优化案例

本文通过食堂窗口规划的真实案例,详细阐述了如何运用排队论中的M/M/S模型量化服务效率。文章从问题拆解、数据采集、模型计算到成本效益分析,提供了完整的数学建模实战指南,帮助读者掌握利用最优化方法解决资源配置问题的核心流程与工具。

docker7nomad的博客 1086

C# OpenCV机器视觉:红外体温检测

阿强长舒一口气,脸上露出了自信的笑容:“好了,万事俱备,只欠东风,接下来就看我的了!在这热图像里,不同的颜色代表着不同的温度区域,就好比一幅用温度绘制的抽象画,红色通常表示高温,蓝色则代表低温,通过解读这幅画,就能大致了解物体表面的温度分布情况。” 阿强兴奋得一拍大腿,“这不就像是找到了隐藏在黑暗中的线索,只要顺着这些红外线的踪迹,就能揪出体温异常的潜在风险,守护大家的健康啦。带着这份坚定的信念,阿强又投入到新的科技研发中,他相信,只要大家齐心协力,用科技点亮希望之光,就一定能迎来更加美好的明天。

xcwzj123的专栏 3152

使用TrieTree(字典树)来实现敏感词过滤

字典树(TrieTree),是一种树形结构,典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串,如01字典树)。主要思想是利用字符串的公共前缀来节约存储空间。很好地利用了串的公共前缀,节约了存储空间。字典树主要包含两种操作,插入和查找。字典树具有以下规则:-1. 根节点不包含字符,其他节点包含一个字符。从根节点到某一节点经过的字符连接起来构成一个字符串。如图中的 him 、 her 、 cat 、 no 、 nova。一个字符串与 Trie 树中的一条路径对应。

weixin_55850954的博客 2065

AI提示词设计中的批判性思维训练方法

在人工智能应用中,提示词(prompt)设计是影响生成内容质量的关键因素。通过引入批判性思维框架,可以有效提升AI输出的逻辑性和多样性。该方法基于目的澄清、证据评估、假设检验和多视角分析四大支柱,构建结构化的问题拆解和证据链。在学术论文辅助写作、商业决策模拟等场景中,这种训练方式能显著提高内容质量,特别适合学生、职场人士和内容生产者。结合思维可视化工具和本地知识库,可以实现从基础应用到专家级分析的进阶。

weixin_33624741的博客 231

Java实现字典树TrieTree

为了准备阿里的网上笔试,这几天回顾了数据结构.看到字典树时,突然发现四六级的高频词可以用字典树找出来的.(应该不会是一个一个数出来的吧....) 构造字典树的过程如下: 1.首先确定树节点需要用怎么样的数据结构,我是这样写的: public class TrieTreeNode { /** * 节点深度 */ public short depth; /** * 存放当前节点

Leo's Blog 7036

[模型部署]-[LLM]昇腾部署Qwen3-Coder-30B-A3B-Instruct

摘要:本文介绍了在Ascend NPU上部署Qwen3-Coder-30B-A3B-Instruct大模型的实践过程。通过vLLM-Ascend框架,详细说明了环境准备(CANN 8.1、PyTorch-NPU 2.5.1等)、模型下载、容器启动及服务部署步骤,包括vLLM服务的启动参数配置和接口测试方法。测试结果显示在当前2卡910B3配置下性能表现仅为8token/s(1并发),并指出流式调用时存在无法输出usage信息的问题。该方案为Ascend平台私有化部署大模型推理服务提供了参考实现。

1673

SpringBoot中使用FastJson解析Json数据

场景 1.SpringBoot默认配置的是Jackson。 2.项目搭建专栏: https://blog.youkuaiyun.com/BADAO_LIUMANG_QIZHI/column/info/35688 实现 引入fastJson的依赖 <!-- fastjson的依赖 --> <dependency> <groupId>com.alibab...

BADAO_LIUMANG_QIZHI的博客 4140

TrieTree字典树数据结构实现

字典树(TrieTree),一般又称为单词查找树,是一种树形结构,常用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。 如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。 优点: 最大限度的减少无谓的比较,查询效率比哈希高。 核心思想: 利用字符串的公共前缀来降低查询时间,达到提高查询的目的。 缺点: 空间消耗比...

weixin_44827160的博客 483

Graphviz安装及使用:决策树可视化

Python大数据分析与机器学习学习笔记 Graphviz安装及使用:决策树可视化

算法工程师 1万+

【数据结构】初入数据结构的字典树 ( Trie Tree ) 及实现

什么是字典树?字典树的定义?字典树的应用?怎么实现一颗字典树?

长路漫漫的歇脚处 898

【数据集】基于ubESTARFM法的100m 地温LST数据集(澳大利亚)

【数据集】基于ubESTARFM法的100m 地温LST数据集(澳大利亚)

WW、forever的博客 266

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

算法学习过程——数据结构之字典树TrieTree

自用复习以及分享给大家,有任何问题可以留言或私信。

2301_76888784的博客 1116

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

TrieTree的实现

TrieTree原理 TrieTree也叫字典树,利用字符串之间的公共前缀,将重复的前缀和并在一起。 其中根节点不包含任何信息。 TrieTree的实现 import java.util.*; class TrieTreeNode{ char data; TrieTreeNode []children=new TrieTreeNode[26]; //每一个node的下标就对应着一个字母(假...

peony的博客 245

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

推荐开源项目:PHP-TrieTree - 强大的字典树实现

推荐开源项目:PHP-TrieTree - 强大的字典树实现 项目介绍 PHP-TrieTree 是一个高效的PHP字典树实现,由开发者 AbelZhou 创建并维护。这个轻量级库提供了一种高效的方式来存储和检索字符串,适用于各种实时和非实时的应用场景。通过Composer轻松安装,支持多种功能,包括命中单个或多个关键字的返回,自定义数组的添加以及关键词的删除等。 项目技术分析 PHP-TrieT...

gitblog_00015的博客 482

字典树(TrieTree)

文章目录一、字典树是什么二、字典树的实现总结 一、字典树是什么 字典树,顾名思义,就是通过树(多叉树)的结构来存储类似字典的内容,主要是存储字符串中的字符,利用字符串的公共前缀来减少查询的时间,也就是常说的空间换时间。根节点不包含任何字符,也可以理解为 ‘/’(类似Linux文件系统中的根目录),从根节点到某一节点可以唯一确定一个字符串。在实际使用中,主要用来查找公共前缀的字符串。 图片来源参考自:https://segmentfault.com/a/1190000022597367 二、字典树的实现 假

yuerenzirao的博客 814

字典树TrieTree(prefixTree)

TrieTreeC++代码 C++代码 #include<cstring> class TrieNode{ pubulic: //可以用 const int static MaxBranchNum = 26; int pass; int end; string word; TrieNode* next[MaxBranchNum]; TrieNode() { pass = 0; edn = 0; word = ""; memset(next, nullptr, siz

zhaoxiaoba的博客 186

前缀树(字典树)trieTree两种实现

实现TrieTree中的易混淆的地方:注意查找字符串和字符串前缀的区别 1.查找字符串的话,要使用最后一个节点判断是否为单词,多种解决办法,可以设置标志位,tree:单词,false:不是单词,是字串的中间部分 或者也可以用第一种实现中的end值,end>0表示字符串结尾 2.查找前缀的话,没条件,只要trietree中有前缀单词即可 最后标注:数据结构的实现是多种多样的,掌握核心的灵魂才是关键 数组实现trieTree:缺点浪费空间,一般不使用 public class TrieTree {

qq_44658941的博客 298
上一篇: 算法学习笔记(10)-缓存淘汰算法LRU
下一篇: LeetCode(8)-回溯:组合排列问题
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值