数据结构(7.5)-- 树扩展之字典树

一、字典树

1、字典树介绍

字典树,也称为“前缀树”,是一种特殊的树状数据结构,对于解决字符串相关问题非常有效。典型

用于统计、排序、和保存大量字符串。所以经常被搜索引擎系统用于文本词频统计。它的优点是:

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

高。

2、字典树的性质

(1)根节点不包含字符,除了根节点每个节点都只包含一个字符。root节点不含字符这样做的目的是为了能够包括所有字符串。

(2)从根节点到某一个节点,路过字符串起来就是该节点对应的字符串。

(3)每个节点的子节点字符不同,也就是找到对应单词、字符是唯一的。

3、字典树的实现

(1)定义多叉树--孩子表示法

struct TreeNode {ELEMENT_TYPE value;    //结点值TreeNode* children[NUM];    //孩子结点
};

(2) 定义字典树

int size = 26;
struct TrieNode {bool isEndOfWord; //记录该结点是否是一个串的结束TrieNode* children[SIZE]; //字母映射表
};

二、面试中关于字典树的常见问题

1、计算字典树中的总单词数

题目:创建一颗字典树,并且计算该字典树中的总单词数。如下字典树,[“bag”, “ban”, “bat”, “big”,“bil”,“bit”],单词数为6。

思路:

首先要创建一颗字典树,在每个节点上,设置一个标记来表示该节点是否是一个单词的结束。

再向字典树中插入单词,从根节点开始,递归地遍历字典树的所有子节点。

最后计算单词数:对于每个子节点,如果其标记为结束,则将计数器加1。最终,计数器的值就是字典树中的总单词数。

#include<iostream>
using namespace std;const int ALPHABET_SIZE = 26;
// TrieNode表示字典树中的节点
struct TrieNode
{bool isEndOfWord; //用于标记该节点是否是一个单词的结束TrieNode* children[ALPHABET_SIZE]; //包含26个子节点的数组// 构造函数TrieNode():isEndOfWord(false){for(int i = 0; i < ALPHABET_SIZE; i++){children[i] = nullptr;}}
};class Trie{private:TrieNode* root;public:Trie(){root = new TrieNode();}// 向字典树中插入单词void insert(string word){TrieNode* current = root;// 遍历字符串中的字符for(char ch: word){int index = ch - 'a';if(current->children[index] == nullptr)current->children[index] = new TrieNode();current = current->children[index];}// 遍历结束,标记该节点单词结束current->isEndOfWord = true;}// 计算字典树中的总单词数int countWords(){int count = 0;countWordsDFS(root, count);return count;}private:// 深度优先搜索(DFS)递归地遍历字典树的所有节点,并在遇到结束节点时将计数器加1。void countWordsDFS(TrieNode* node, int& count){if(node == NULL)return;if(node->isEndOfWord) count += 1;for(int i = 0; i < ALPHABET_SIZE; i++){if(node->children[i] != NULL)countWordsDFS(node->children[i], count);}}};int main(){Trie trie;trie.insert("bag");trie.insert("ban");trie.insert("bat");trie.insert("big");trie.insert("bil");trie.insert("bit");int count = trie.countWords();cout << "字典树中的总单词数:" << count << endl;
}

2、查找字典树中某个单词是否存在 

题目:输入ban,返回true;输入bad,返回False。

思路:插入操作类似。

从字典树的根节点依次遍历单词中的字符,如果当前节点的子节点中,不存在键为 ch 的节点,则说明不存在该单词,直接返回 False。如果当前节点的子节点中,存在键为 ch 的节点,则令当前节点指向新建立的节点,然后继续查找下一个字符。在单词处理完成时,判断当前节点是否有单词结束标记,如果有,则说明字典树中存在该单词,返回 True。否则,则说明字典树中不存在该单词,返回 False。

#include<iostream>
using namespace std;const int ALPHABET_SIZE = 26;
// TrieNode表示字典树中的节点
struct TrieNode
{bool isEndOfWord; //用于标记该节点是否是一个单词的结束TrieNode* children[ALPHABET_SIZE]; //包含26个子节点的数组// 构造函数TrieNode():isEndOfWord(false){for(int i = 0; i < ALPHABET_SIZE; i++){children[i] = nullptr;}}
};class Trie{private:TrieNode* root;public:Trie(){root = new TrieNode();}// 向字典树中插入单词void insert(string word){TrieNode* current = root;// 遍历字符串中的字符for(char ch: word){int index = ch - 'a';if(current->children[index] == nullptr)current->children[index] = new TrieNode();current = current->children[index];}// 遍历结束,标记该节点单词结束current->isEndOfWord = true;}// 查找字典树中某个单词是否存在bool searchWord(string word){TrieNode* current = root;for(char ch: word){int index = ch - 'a';if(current->children[index] == NULL)return false; //如果当前节点的子节点中,不存在键为 ch 的节点,直接返回falsecurrent = current->children[index];}// 判断当前节点是否为空,并且是否有单词结束标记return (current->isEndOfWord & current != NULL);}};int main(){Trie trie;trie.insert("bag");trie.insert("ban");trie.insert("bat");trie.insert("big");trie.insert("bil");trie.insert("bit");string word = "bad";bool b = trie.searchWord(word);if(b)cout << "该字典树存在" << word;elsecout << "该字典树不存在" << word;}

3、查找字典树中某个前缀是否存在

在字典树中查找某个前缀是否存在,和字典树的查找单词操作一样,不同点在于最后不需要判断是否有单词结束标记。

// 查找字典树中某个前缀是否存在bool searchWord(string word){TrieNode* current = root;for(char ch: word){int index = ch - 'a';if(current->children[index] == NULL)return false; //如果当前节点的子节点中,不存在键为 ch 的节点,直接返回falsecurrent = current->children[index];}// 判断当前节点是否为空return current;}

4、打印存储在字典树中的所有单词

题目:如下字典树,打印[ bag,ban,bat,big,bil,bit ]。

思路:在哈希遍历树的完整路径中,我们使用先序遍历逐步构建叶子节点的路径。本题类似,使用

深度遍历递归地遍历字典树的所有子节点,并存储每个叶子节点的字符串。遇到叶子节点(即

isEndOfWord=true),则打印存储在容器里的字符串(字符串组合起来就是一个完整单词)。

#include<iostream>
using namespace std;const int ALPHABET_SIZE = 26;
// TrieNode表示字典树中的节点
struct TrieNode
{bool isEndOfWord; //用于标记该节点是否是一个单词的结束TrieNode* children[ALPHABET_SIZE]; //包含26个子节点的数组// 构造函数TrieNode():isEndOfWord(false){for(int i = 0; i < ALPHABET_SIZE; i++){children[i] = nullptr;}}
};class Trie{private:TrieNode* root;public:Trie(){root = new TrieNode();}// 向字典树中插入单词void insert(string word){TrieNode* current = root;// 遍历字符串中的字符for(char ch: word){int index = ch - 'a';if(current->children[index] == nullptr)current->children[index] = new TrieNode();current = current->children[index];}// 遍历结束,标记该节点单词结束current->isEndOfWord = true;}void printWord(){string arr;print(root, arr);}private: void print(TrieNode* node, string& arr){if(node->isEndOfWord){for(auto a: arr)cout << a;cout << "," ;}for(int i = 0; i < ALPHABET_SIZE; i++){if(node->children[i] != NULL){char ch = i + 'a';arr.push_back(ch);print(node->children[i], arr);}}arr.pop_back();}
};int main(){Trie trie;trie.insert("bag");trie.insert("ban");trie.insert("bat");trie.insert("big");trie.insert("bil");trie.insert("bit");trie.printWord();
}

 

5、使用字典树对数组的元素进行排序

题目:

input:arr = ['apple', 'banana', 'application']

output:arr = ['apple', 'application', 'banana']

思路:同第二题,因为字典树节点的顺序已经确定,所以,遍历出来的单词即是排序后的单词组。

#include<iostream>
#include<vector>
using namespace std;const int ALPHABET_SIZE = 26;
// TrieNode表示字典树中的节点
struct TrieNode
{bool isEndOfWord; //用于标记该节点是否是一个单词的结束TrieNode* children[ALPHABET_SIZE]; //包含26个子节点的数组// 构造函数TrieNode():isEndOfWord(false){for(int i = 0; i < ALPHABET_SIZE; i++){children[i] = nullptr;}}
};class Trie{private:TrieNode* root;public:Trie(){root = new TrieNode();}// 向字典树中插入单词void insert(string word){TrieNode* current = root;// 遍历字符串中的字符for(char ch: word){int index = ch - 'a';if(current->children[index] == nullptr)current->children[index] = new TrieNode();current = current->children[index];}// 遍历结束,标记该节点单词结束current->isEndOfWord = true;}vector<string> Order(){string arr;vector<string> s;traverse(root, arr, s);return s;}private: // arr:连接字符串组成的单词,res:排序后的数组void traverse(TrieNode* node, string& arr, vector<string>& res){if(node->isEndOfWord){res.push_back(arr);}for(int i = 0; i < ALPHABET_SIZE; i++){if(node->children[i] != NULL){char ch = i + 'a';arr.push_back(ch);traverse(node->children[i], arr, res);}}arr.pop_back();}
};int main(){Trie trie;vector<string> arr = { "apple", "banana", "application" };for (const string& word : arr)trie.insert(word);vector<string> sortedArr = trie.Order();std::cout << "排序后的数组元素:" << std::endl;for (const std::string& word : sortedArr)std::cout << word << std::endl;
}

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

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

相关文章

(1)(1.8) MSP(MultiWii 串行协议)(4.1 版)

文章目录 前言 1 协议概述 2 配置 3 参数说明 前言 ArduPilot 支持 MSP 协议&#xff0c;可通过任何串行端口进行遥测和传感器。这允许 ArduPilot 将其遥测数据发送到 MSP 兼容设备&#xff08;如大疆护目镜&#xff09;&#xff0c;用于屏幕显示&#xff08;OSD&#xff…

VR智慧眼:为各行业打造3D数字化业务协同平台

自改革开放以来&#xff0c;城镇化建设一直在不断推进实施&#xff0c;如今各城市化速度虽然在不断加快&#xff0c;但随之而来的部分城市开始出现资源短缺、环境污染、交通拥堵、安全隐患等问题&#xff0c;因此为了满足智慧城市大型区域场景数字化升级需求&#xff0c;助力区…

【深入浅出SpringCloud源码探究】「Netflix系列之Ribbon+Fegin」微服务化的负载均衡组件源码剖析与实战开发全流程(Ribbon篇)

微服务化的负载均衡组件源码剖析与实战开发全流程 什么是负载均衡负载均衡的种类服务器端负载均衡&#xff08;S-LB&#xff09;客户端负载均衡&#xff08;C-LB&#xff09;注解LoadBalancedLoadBalancerAutoConfiguration类LoadBalancerClient类源码分析 ServiceInstanceChoo…

ToolLLM model 以及LangChain AutoGPT Xagent在调用外部工具Tools的表现对比浅析

文章主要谈及主流ToolLLM 以及高口碑Agent 在调用Tools上的一些对比&#xff0c;框架先上&#xff0c;内容会不断丰富与更新。 第一部分&#xff0c;ToolLLM model 先来说主打Function Call 的大模型们 OpenAI GPT 宇宙第一LLM&#xff0c;它的functionCall都知道&#xff0…

python 小程序学生选课系统源码

开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 学生&#xff1a; 登录&#xff0c;选课&#xff08;查看课程及选择&#xff09;&#xff0c;我的成绩&#xff0c;…

Axure中动态面板使用及轮播图多种登录方式左侧导航栏之案列

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、轮播图简介 1、什么是轮播图 2、轮播图有什么作用 3、轮播图有什么特点 4、轮播图适应范围 5、…

解决Chrome同一账号在不同设备无法自动同步书签的问题

文章目录 一、问题与原因&#xff1f;2. 解决办法 一、问题与原因&#xff1f; 1.问题 使用谷歌Chrome浏览器比较头疼的问题就是&#xff1a;使用同一个Google账号&#xff0c;办公电脑与家用电脑的数据无法同步。比如&#xff1a;办公电脑中的书签、浏览记录等数据&#xff0…

C语言----文件操作(二)

在上一篇文章中我们简单介绍了在C语言中文件是什么以及文件的打开和关闭操作&#xff0c;在实际工作中&#xff0c;我们不仅仅是要打开和关闭文件&#xff0c;二是需要对文件进行增删改写。本文将详细介绍如果对文件进行安全读写。 一&#xff0c;以字符形式读写文件&#xff…

Nessus漏洞扫描报错:42873 - SSL Medium Strength Cipher Suites Supported (SWEET32)

个人搭建的windows server 2019服务器,被Nessus工具扫描出现三个漏洞,修复比较过程比较坎坷,特记录下 首先:报错信息: 42873 - SSL Medium Strength Cipher Suites Supported (SWEET32) 104743 - TLS Version 1.0 Protocol Detection 157288 - TLS Version 1.1 Protocol …

uni-app微信小程序隐藏左上角返回按钮

官方文档链接&#xff1a;uni.setNavigationBarTitle(OBJECT) | uni-app官网 (dcloud.net.cn) 首先要明确的是页面间的跳转方式有几种、每一种默认的作用是什么。 uniapp五种跳转方式 第一&#xff1a;wx.navigatorTo 【新页面打开&#xff0c;默认会有返回按钮】第二&#x…

阿里云服务器ECS安全组开启端口教程

阿里云服务器安全组开启端口教程 云服务器 ECS&#xff08;Elastic Compute Service&#xff09; 云服务器 ECS&#xff08;Elastic Compute Service&#xff09;是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;助您降低 IT 成本&#xff0c;提升运维效率&#xff0c;使您…

ACT、NAT、NATPT和EASY-IP

目录 一、ACL 1.ACL 2.ACL的两种应用匹配机制 3.ACL的基本类型 4.ACL命令操作 5.ACL实验&#xff1a; 4.ACL的应用原则&#xff1a; 5.匹配原则&#xff1a; 二、NAT 1.NAT的原理及作用&#xff1a; 2.NAT分类 3.NAT配置 三、EASY-ip实验 四、NATPT 五、通配符 …

如何用 Cargo 管理 Rust 工程系列 乙

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/__nvVZYti-G05QJHIp_f8Q 编译程序 这次我们用 cargo 来启动编译&#xff0c;cargo 提供了 build 指令来调度工具构建并输出软件。cargo build 只…

【Docker六】Docker-consul

目录 一、docker-consul概述 1、服务注册和发现&#xff1a; 1.1、服务注册和发现概念 1.2、服务注册和发现工作机制&#xff1a; 1.3、服务注册与发现的优点&#xff1a; 2、docker-consul概念 2.1、consul的主要特点&#xff1a; 二、consul架构部署&#xff1a; 1、…

构建强大应用的引擎:深度解析Spring Boot Starter机制

目录 引言1. Spring Boot Starter机制1.1 什么是Spring Boot Starter1.2 为什么要使用Spring Boot Starter1.3.应用场景1.4.自动加载核心注解说明 2. 综合案例配置类制作控制功能实现 总结 引言 在当今互联网时代&#xff0c;构建高性能、可维护的应用已成为开发者的首要任务。…

利用有限制通配符来提升API的灵活性

在Java中&#xff0c;有限制通配符&#xff08;bounded wildcard&#xff09;允许你在泛型中指定一个范围&#xff0c;从而提升API的灵活性。通配符使得你能够编写更通用、适用于多种类型的代码。以下是一个利用有限制通配符提升API灵活性的例子&#xff1a; 假设有一个简单的…

QT笔记(节选)具体图片等下载资源

QT笔记&#xff08;节选&#xff09;具体图片等下载资源 根据b站视频做的笔记&#xff1a; https://www.bilibili.com/video/BV1g4411H78N?p44&spm_id_frompageDriver&vd_sourcea3e6a48ccd3d7d1f969f662653ed68c9 qt是一个跨平台的c图形用户界面应用程序框架&#x…

【最新版】PyCharm基础调试功能详解

文章目录 一、断点1. 断点的类型a. 行断点b. 异常断点 2. 设置断点a. 设置行断点b. 设置异常断点 3. 管理断点a. 删除断点b. 将断点静音 二、调试功能0. 测试代码1. 设置断点2. 调试的多种启动方式3. 观察调试控制台a. 步过b. 步入c. 单步执行代码d. 步出e. 运行到光标处f. 重新…

LVS简介及LVS-NAT负载均衡群集的搭建

目录 LVS群集简介 群集的含义和应用场景 性能扩展方式 群集的分类 负载均衡&#xff08;LB&#xff09; 高可用&#xff08;HA&#xff09; 高性能运算&#xff08;HPC&#xff09; LVS的三种工作模式 NAT 地址转换 TUN IP隧道 IP Tunnel DR 直接路由 Direct Rout…

Leaflet.Graticule源码分析以及经纬度汉化展示

目录 前言 一、源码分析 1、类图设计 2、时序调用 3、调用说明 二、经纬度汉化 1、改造前 2、汉化 3、改造效果 总结 前言 在之前的博客基于Leaflet的Webgis经纬网格生成实践中&#xff0c;已经深入介绍了Leaflet.Graticule的实际使用方法和进行了简单的源码分析。认…