概述:

说来也奇怪,最近碰到的很多问题都需要用字典树来解决,索性就来研究一番。在这篇博客中,我会通过一些实例来讲解一下字典树的一些基本使用。例如:创建、添加、查找、按字典序排序、按数值大小进行排序(对于一些数值序列的排序)等等。

关于字典的实际应用实例,请参见本人的另一篇博客:《算法:两种对拼音进行智能切分的方法》

本文链接:http://blog.csdn.net/lemon_tree12138/article/details/49177509 -- 编程小笙
                                                                 --转载请注明出处

版权说明

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
本文作者:Q-WHai
发表日期: 2015年10月19日
本文链接:https://qwhai.blog.csdn.net/article/details/49177509
来源:CSDN
更多内容:分类 >> 算法与数学

基本使用:

0.要点说明:

为了便于以下对字典树的说明,我这里的节点Node可能会有一些对于读者而并不必要的成员。例如,fre, visited, minLength, prefixCount等等。这里读者可以根据自己的需求自行增减。

String name; // 结点的字符名称int fre; // 单词的词频boolean end; // 是否是单词结尾boolean root; // 是否是根结点Node[] children; // 子节点信息boolean visited; // 是否已经遍历过了int minLength; // 通过该节点的最小的数字长度int prefixCount = 0; // 有多少单词通过这个节点,即节点字符出现的次数Node parent; // 当前节点的父节点

1.创建一棵新的字典树

对于创建一棵空的字典树,其实相对来说是比较容易的。因为,我们不需要对树进行一些元素新增或是移除。我们只是对字典树中的一些必要的成员进行了一些初始化的工作。下面是代码部分:

Node root;int depth;public TrieTree(String name) {root = new Node(name);root.setFre(0);depth = 0;root.setEnd(false);root.setRoot(true);}

2.插入一个新的节点元素

对于在字典树的新增元素的关键地方,应该就是我们要在何是停止,即新增完成的条件是什么?

我们元素新增完成的条件是,我们在对新增的元素(如:"12345")进行遍历,直到遍历到字符串的末尾。

在我们对新增的字符串str进行插入字典树的过程中,比如说已经遍历到位置i,如果str.chartAt(i)在字典树中已经存在,则我们可以直接pass,继续遍历i+1的位置;如果str.chartAt(i)在字典树中是不存在的,那么我们就必须新增此节点,再将新增的此节点挂载到上一个点的后面,然后继续遍历i+1位置上元素str.chartAt(i+1)。然后在插入的字符串最后的位置上设置此节点为结束节点(即在此位置可以构成单词,或是完整添加的数字)。具体代码如下:

public void insert(String number) {Node node = root;char[] numberCells = number.toCharArray();for (int i = 0; i < numberCells.length; i++) {int num = Integer.parseInt(String.valueOf(numberCells[i]));if (node.getChildren()[num] != null) {if (numberCells.length < node.getChildren()[num].getMinLength()) {node.getChildren()[num].setMinLength(numberCells.length);}if (i == numberCells.length - 1) {Node endNode = node.getChildren()[num];endNode.setFre(endNode.getFre() + 1);endNode.setEnd(true);}node.getChildren()[num].prefixCountIncrement();} else {Node newNode = new Node(numberCells[i] + "");newNode.setParent(node);if (i == numberCells.length - 1) {newNode.setFre(1);newNode.setEnd(true);newNode.setRoot(false);}newNode.setMinLength(numberCells.length);node.getChildren()[num] = newNode;depth = Math.max(i + 1, depth);}node = node.getChildren()[num];}}

3.返回Trie中某一节点被添加的次数

此功能的应用点在于,词频统计。我们在每次新增一个元素时都会在原来的基本上,对词频进行自增处理。如果新增的词在之前的字典树中是不存在的,就设置初始值为1,如果原本有这个节点,就在原来的词频上+1.在上一步(插入一个新的节点元素)中可以看到具体操作。那么这里介绍一下查询词频的操作。代码如下:

public int searchFre(String number) {int fre = -1;Node node = root;char[] numberCells = number.toCharArray();for (int i = 0; i < numberCells.length; i++) {int num = Integer.parseInt(String.valueOf(numberCells[i]));if (node.getChildren()[num] != null) {node = node.getChildren()[num];fre = node.getFre();} else {fre = -1;break;}}return fre;}

4.计算有多少个单词以prefix为前缀

对于前缀统计的操作,我们也需要在插入的过程中进行统计。这是因为,如果我们在插入的时候不进行统计,那么我们就必须在每次查询一个前缀的时候,去遍历前缀结束节点以下的所有子节点。这样势必会增加时间上的复杂度,是一种不理想的方式。不过,因为有时,我们并不会只是要求计算有多少以prefix为前缀的串。所以,可能遍历是在所难免。还是要看需求吧。以下代码是查询过程:

public int countPrefix(String prefix) {if (prefix == null || prefix.length() == 0) {return -1;}Node node = root;char[] letters = prefix.toCharArray();for (int i = 0; i < prefix.length(); i++) {if (node.getChildren()[Integer.parseInt(String.valueOf(letters[i]))] == null) {return 0;} else {node = node.getChildren()[Integer.parseInt(String.valueOf(letters[i]))];}}return node.getPrefixCount();}

5.获得trie的深度

对于树深度的问题,对于其实际的应用点,我目前还未知晓。只是在写其他功能的时候想到了,就附带了吧。

这个深度,也是要在新增节点的时候去实时更新的。这样可以减小查询时的时间复杂度。查询代码如下:

public int depth() {return depth;}

It's too easy, isn't it?

6.对字典树进行字典序排序(即深度优先搜索)

就以我们的数字字典树为例。因为我们在构造树的过程就是一个以字典序为基础的过程,所以我们的遍历就可以直接对树进行顺序遍历就Ok。对于字典树而言,顺序遍历的过程,其实就是对树的深度遍历。如果大家还记得深度遍历的过程,相信大家可以很容易地写出此代码。我的编码过程如下(使用了递归):

public void dictOrder(Node node, String prefix) {if (node != null) {if (node.isEnd()) {System.out.println(prefix + node.getName());}for (Node children : node.getChildren()) {if (children == null) {continue;}dictOrder(children, prefix + (node.isRoot() ? "" : node.getName()));}}}

7.对数字字典树按实际数值大小排序(即广度优先搜索)

在第6步中,我们看到对树深度优先搜索的过程就是对树进行按字典序排序。那么可能你也会问另一个问题,那么是广度优先搜索又会是怎么样的结果呢?广度优先搜索的另一个叫法,我们可以说是对树的分层遍历。既然是对树进行分层,那么就是说"123"要排在"1234"的前面。而在第6步中我们也说到了,字典树本身就是以字典序为基础进行新增。也就是"123"必然是在"124"的前面。Ok,基于这样的分析,我们可以得到一个很容易理解的结论:对字典树进行广度优先搜索的过程就是对字典树进行按数值大小进行排序。具体的实现代码如下:

/*** 对数字字典树按实际数值大小排序(即分层打印)* TrieTree*/public void sortNumberOrder(Node node, String prefix) {Queue<Node> queuing = new LinkedList<Node>();queuing.offer(node);while (!queuing.isEmpty()) {Node currentNode = queuing.poll();if (currentNode.isEnd()) {System.out.println(getNodePath(currentNode));}Node[] children = currentNode.getChildren();for (Node sonNode : children) {if (sonNode != null) {queuing.offer(sonNode);}}}}/*** 获得某一节点的上层节点,即前缀字符串* @param node* @return*/public String getNodePath(Node node) {StringBuffer path = new StringBuffer();Node currentNode = node;while (currentNode.getParent() != null) {path.append(currentNode.getName());currentNode = currentNode.getParent();}return path.reverse().toString();}

Github

https://github.com/qwhai/simple-tree

数据结构:字典树的基本使用相关推荐

  1. 【前缀树】C++ 数据结构 字典树

    之前有段时间力扣天天出字典树,当时写得特别熟练,几个月没做都忘得差不多了--今天又出了相关题目,正好复习一下. 文章目录 一.前缀树是什么? 二.实现前缀树 三.例题:添加与搜索单词 总结 一.前缀树 ...

  2. def python语言对照表_如何用python,华丽实现字典树?

    文/IT可达鸭 图/IT可达鸭.网络 前言 上篇文章,我们用自定义数据结构实现了有序集合set.本文在此基础上,进一步加深,设计一个新的数据结构--字典树.有过参加过算法竞赛或做过数据检索的小伙伴,应 ...

  3. 字典树应用——词频统计 (C++实现)

    来学校交流学习的第一个正式的小项目作业就是软件工程老师所提出的词频统计了,具体要求如下. 要求: 写一个程序,分析一个文本文件中各个词出现的频率,并且把频率最高的10个词打印出来.文本文件大约是30K ...

  4. c语言 trie树,数据结构篇——字典树(trie树)

    引入 现在有这样一个问题, 给出$n$个单词和$m$个询问,每次询问一个单词,回答这个单词是否在单词表中出现过. 好像还行,用 map ,几行就完事了. 那如果n的范围是 $10^5$ 呢?再用 $m ...

  5. 《恋上数据结构第1季》字典树 Trie

    字典树Trie Trie 简介 Trie 实现 接口设计 源码 测试 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java 基础推荐看这个: Java 强化笔记目录 Tri ...

  6. 自学Java day53 使用jvav实现 字典树 数据结构 从jvav到架构师

    字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公 ...

  7. 数据结构与算法(十一)Trie字典树

    本文主要包括以下内容: Trie字典树的基本概念 Trie字典树的基本操作 插入 查找 前缀查询 删除 基于链表的Trie字典树 基于Trie的Set性能对比 LeetCode相关线段树的问题 Lee ...

  8. 分门别类刷leetcode——高级数据结构(字典树,前缀树,trie树,并查集,线段树)

    目录 Trie树(字典树.前缀树)的基础知识 字典树的节点表示 字典树构造的例子 字典树的前序遍历 获取字典树中全部单词 字典树的整体功能 字典树的插入操作 字典树的搜索操作 字典树的前缀查询 字典树 ...

  9. 数据结构之字典树Trie

    文章目录 Trie 字典树 前缀树 什么是Trie 基本概念 基本性质 应用场景 优点 手写一个trie Trie字典树的前缀查询 实现Trie(前缀树) LeetCode208 添加与搜索单词 - ...

  10. 基础数据结构(二):字典树、并查集、堆、哈希表、字符串的哈希方式、STL的常见容器及其接口

    文章目录 一.字典树Trie 1 原理 2 Trie字符串统计 3 [LeetCode 208. 实现 Trie (前缀树)](https://leetcode-cn.com/problems/imp ...

最新文章

  1. 使用python愉快地做高数线代题目~
  2. 虚拟机下CentOS 6.5配置IP地址的三种方法
  3. AutoCAD.net/Map 3D/AIMS/MapGuide/Civil 3D二次开发学习指南
  4. 每日一皮:小公司搞中台的真实写照..
  5. C# 8中的范围类型(Range Type)
  6. Shiro设定密码匹配规则(自定义密码验证匹配器)
  7. Ubuntu 16.04 升级到内核4.18 后 vmplayer 不能运行
  8. WinCE 5.0下的鼠标键盘驱动分析
  9. 神经网络加速器设计研究:寒武纪DianNao论文阅读
  10. 计算机-国家精品课程-推荐
  11. 计算机网络:逆向工程(精细版)
  12. 干货 | PCB多层板为什么都是偶数层?奇数层不行吗?
  13. python进行邮件文件.eml.pst.msg信息提取(包括附件)
  14. ML - 线性回归(Linear Regression)
  15. 阿里云Centos镜像虚拟机安装方法
  16. 常用测试网络联通方法
  17. mysql异地灾备架构_最佳实践 数据库异地灾备
  18. vue3的pinia详解
  19. tcpdump抓包,并保存为文件
  20. 使用MATLAB Coder将工具箱中的系统对象转换为C代码

热门文章

  1. 自动化测试之iframe窗口的切换
  2. MySQL—数据库表的完整性约束(非外键约束)
  3. PKCS#11 in OP-TEE
  4. 2021-07-03
  5. Wireshark抓取数据包
  6. ctf之py反编译求p*q%n==1
  7. ubuntu第一次设置root密码
  8. 2、事务的概念和特性
  9. Java非线程安全问题的解决方法
  10. 判断用户输入的日期是否为当前日期