第十章:字典树(trie)与并查集

news2024/12/29 10:57:28

第十章:字典树(trie)与并查集

  • 一、字典树(trie)
    • 引入
    • 1、什么是字典树?
    • 2、思路分析
    • 3、复杂度分析
    • 4、模板
      • (1)问题:
      • (2)模板:
      • (3)模板代码解释:
        • 变量解释:
        • 插入函数解释:
        • 搜索函数解释:
  • 二、并查集(找祖宗算法)
    • 1、什么是并查集
    • 2、思路分析
    • 3、算法优化——路径压缩
    • 4、模板
      • (1)问题:
      • (2)模板:
      • (3)模板解释:
        • 变量解释:
        • find函数解释:

一、字典树(trie)

引入

假设我们编写一个程序来实现一个词典,那么这个词典的所需要实现的两个功能就是插入和搜索。插入的话,我们最容易想到的就是创建一个字符指针数组,然后让这个数组去记录每一个单词的地址。但是当我们去搜索某个单词的时候,时间复杂度是O(N2,效率是非常低下的,因此为了优化这个问题,就出现了我们今天介绍的字典树。

1、什么是字典树?

我们在查字典的时候,先按照首字母去查,然后再去找剩下的字母。因此我们就知道字典树是一种搜索的算法,他是一种用来搜索的算法。

其逻辑结构如下图所示:
请添加图片描述

2、思路分析

我们继续看上面的图,这些图组成了一棵“树”(倒立的),这棵树又节点和边组成。那么我们的字符串中的字符就记录在这些边中,也就是说,我们从父节点0出发,沿着一条名字为“a”的边找到了节点1,然后再从节点1出发沿着一条名字为“p”的边找到了第二个节点,以此类推…假设我们想要查找一个字符串,其实本质上就是给出了一条路,按照这条路上的各个边,我们都能找到节点,那就说明这个字符串是存在的。假设我们按照字符串所指出的路,去找节点,我们发现沿着这条路中的边去走的时候,找不到节点了,那就说明这个字符串是不存在的。

3、复杂度分析

我们利用这个搜索算法主要执行两种操作:一种是完善我们的字典,即插入字符串,另外一种是去搜索。

插入操作:其实就是一个搭建节点的过程,时间复杂度是O(N)

搜索操作:搜索的时间复杂度是O(N)

4、模板

(1)问题:

在这里插入图片描述

(2)模板:

#include<iostream>
using namespace std;
const int N=200010;
char str[100010];
int cnt[N],son[N][26],idx;
void insert(char*str)
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';
        if(!son[p][u])son[p][u]=++idx;
        p=son[p][u];
    }
    cnt[p]++;
}
int quary(char*str)
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';
        if(!son[p][u])return 0;
        p=son[p][u];
    }
    return cnt[p];
}

int main()
{
    int n;
    cin>>n;

    while(n--)
    {
        char op;
        cin>>op>>str;
        if(op=='I')
        {
            insert(str);
        }
        else
        {
            printf("%d\n",quary(str));
        }
    }
    return 0;
}

(3)模板代码解释:

变量解释:

son[x][r]=y:这个变量的意思是我们通过当前的节点x,沿着r这条路线到达节点y。
借此我们就能够理解son[N][26],节点个数的最大值是N,由于我们的路线是通过26个字母来说明的,所以从当前节点到下一个节点之间最多有26条路线。

idx : idx是给用来给节点编号的,因为我们每一个节点是不同的,所以我们通过++idx来给每次创建的子节点来编号。

cnt[p]:这里的p指的是我们当前的节点,当我们有一个单词在p节点处结束的时候,我们就在这里做一个标记,为什么要这么做呢?假设我们存储一个单词:apple,但是我们查询的是app,app仅仅是apple的一个前缀,但是在字典树中并不存在这个单词,仅仅是存在app这条路。因此,我们需要走到这条路的末尾的时候,通过cnt[p]来判断一下,是否真的存在这个单词。简单的来说,idx就是来给每个单词的末尾打上一个标记。

插入函数解释:

我们先把字母转化成0到25的整形数据。
接着我们从根节点出发,通过单词的路线去寻找节点,如果不存在我们就创建一个子节点,然后通过idx给新的节点编号。最后在这个单词末尾节点处,通过cnt来打上标记。

搜索函数解释:

一个单词存在,必须满足两个条件,按照单词的路线,能够找到每一个节点。并切在末尾节点处存在标记。

我们先利用所查单词去寻找节点,如果一个节点不存在,即:son[p][u]==0,说明这里没有节点,那么这个单词必定是不存在的。假设按照单词所说的路线去寻找,我们能够找到每个节点,我们在通过cnt[p]判断一下,这个单词是真的存在,还是说这个单词仅仅是其他单词的前缀。

二、并查集(找祖宗算法)

1、什么是并查集

并查集是一个树形的数据结构,那么其作用其实就是并和查。并的意思就是合并两个树,查的意思就是判断某个节点否属于这棵树。

2、思路分析

其实就类似于家族的概念,合并就是将两个家族合并,我们只需要将让B家族的祖宗认A家族的祖宗为祖宗,那么A和B家族就都属于A家族了。

假设我们想判断一个人是否属于这个家族,我们可以去查这个人的祖宗是不是这个家族的创始人,即可判断。

在这里插入图片描述

3、算法优化——路径压缩

当我们想判断一个子节点是否属于该树的时候,我们需要去找他的祖宗,随着树的层数越多,我们需要回溯的次数也就越多。因此,为了高效地判断,我们可以在找找到一个数的祖宗后,直接将其父节点该成祖宗节点,这样我们的查找算法的时间复杂度就近似于O(1)
在这里插入图片描述

4、模板

(1)问题:

在这里插入图片描述

(2)模板:

#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N];

int find(int x)
{
    if(p[x]!=x)return p[x]=find(p[x]);
    return p[x];
}

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)p[i]=i;
    while(m--)
    {
        char op[2];
        int x,y;
        scanf("%s%d%d",op,&x,&y);
        if(op[0]=='M')
        {
            p[find(x)]=find(y);
        }
        else
        {
            if(find(x)==find(y))puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

(3)模板解释:

变量解释:

int p[N] :我们假设写一个p[1]=2,这一行代码的意思就是,节点1的父节点是2。那么祖宗节点怎么办呢?我们可以将祖宗节点的父节点设置为自身。即p[x]=x,这也是我们对所有节点初始化的操作。

find函数解释:

find函数是用来找祖宗用的。我们采用了一个递归的写法。同时,我们再递归的同时还写了这样一行语句:p(x)=find(x),就是将x的父节点设置为祖宗。这样的话就完成了路径压缩的优化。

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

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

相关文章

vscode开发高频、通用插件集合(精选15个)

vscode开发通用插件集合1.Live Serve2. Chinese3. GitLens4. Color Highlight5. Highlight Matching Tag6. any-rule7. Time Master 或 Code Time8. Vetur9. ESLint10. Vue-format11. ENV12. background13. Code Runner14. Local History15. Postcode此文仅是本人多年的一些经验…

在全链路追踪中加入对方法(Method)追踪

在全链路追踪中加入对方法(Method)追踪 全链路追踪主要是在微服务场景下&#xff0c;实现了服务和服务之间的调用链关系。 这次尝试一下在单体应用中&#xff0c;怎么在全链路追踪技术中加入对方法(Method)追踪。 单体应用是用Springboot开放的一个简单CRUD应用&#xff0c;全…

.NET 企业基本通用权限框架系统源码

源码分享&#xff01; 系统介绍&#xff1a; 1、组织机构多级树型显示&#xff0c;各级部门从属关系一目了然操作便捷 2、用户所有的权限最终分配给用户&#xff0c;如果按用户去分配权限会把系统管理员给累死&#xff0c;系统中先建立角色&#xff0c;角色中再分配权限&#x…

一种词库的比对、保存方式

一种词库的比对、保存方式 词库以树状链表存储&#xff0c;示意图如下&#xff1a; 对于词库&#xff1a;&#xff5b;A,AB,ABC,ADE&#xff5d;可以按以下方式存储 注&#xff1a;每个链表在末尾添加\0表示结束 1 数组形式存储的空间复杂度为O(N^2) 即O(N*M) -N为敏感词长…

vscode配置linux私钥远程免密登录

安装romote-ssh 将linux下的 ssh-keygen -t rsa 生成的密钥id_rsa放到windows的目录下&#xff1a; 在vscode中配置文件路径&#xff1a; 修改配置文件的权限为666&#xff1a; chmod 666 id_rsa_179

电脑录屏快捷键是什么?电脑录屏是什么键

​在日常的生活之中&#xff0c;电脑录屏是比较常用的功能。有些小伙伴知道如何使用电脑自带的录屏软件&#xff0c;可普通的操作步骤实在是有些繁琐&#xff0c;想要通过录屏快捷键&#xff0c;快速进行录屏操作。那么电脑录屏快捷键是什么&#xff1f;电脑录屏是什么键&#…

【SpringMVC】提问问题汇总

【SpringMVC】提问问题汇总&#xff08;1&#xff09;什么是Spring MVC &#xff1f;对springMVC的理解?&#xff08;2&#xff09;SpringMVC的流程&#xff1f;&#xff08;3&#xff09;Springmvc的重要组件&#xff08;3&#xff09;Springmvc的优点&#xff08;设计模式&a…

工程机械流通行业BI经营分析框架(一)四大关注方向

工程机械流通行业的商业智能BI经营分析框架大体可以从四大方向出发来进行整体规划&#xff0c;厂商目标、业务经营目标、战略目标和行业数据这四部分内容。核心还是企业的业务经营目标&#xff0c;但是和其它三类也有很大的关系&#xff0c;所以这四部分需要放在一起去看、去规…

Java多线程(二)

目录 一、线程的使用 Thread类的有关方法 线程的调度 调度策略&#xff1a; java的调度方法 线程的优先级 线程的优先等级 如何获取优先级 线程有关方法及线程优先级练习 线程的分类 二、线程的生命周期 三、线程的同步&#xff08;一&#xff09;&#xff08;线程安…

【Linux】基本指令(二)

文章目录rmdir&&rm 指令nano 指令whoami 指令man 指令cp 指令mv 指令echo 指令cat 指令wc 指令more 指令less 指令head 指令tail 指令date 指令cal 指令rmdir&&rm 指令 &#x1f495; rmdir是一个与mkdir相对应的命令。 mkdir是建立目录&#xff0c;而rmdir是…

UML之类图

概要 类图以反映类的结构(属性、操作)以及类之间的关系为主要目的&#xff0c;描述了软件系统的结构&#xff0c;是一种静态建模方法。类图中的“类”与面向对象语言中的“类”的概念是对应的&#xff0c;是对现实世界中的事物的抽象。元素解析 类 从上到下分为三部分&#…

Jetson Nx 串口接收数据丢失首字节问题

1 问题描述 I write a uart program using c on Jetson Nx(Jetpack 4.6.1,Ubuntu version 18.04 LTS) to communicate with a PC. On PC there’s a uart simulator( as below figure 1) sending data at a period of one second, 30 bytes data are : EB90021112131415161718…

VUE综合数据库编程

VUE综合数据库编程 案例要求 基于node expressvue clielementUImysql&#xff0c;在如图8.14所示的功能的基础上增加一个输入框用于输入商品的id&#xff0c;增加一个“删除”按钮&#xff0c;完成根据id删除商品的功能&#xff08;删除后的结果通过查看数据表goods的最新数据…

LinkedIn领英怎么避免封号?封号怎么解决?(建议收藏)

使用领英的朋友都知道&#xff0c;领英是很容易封号的。辛辛苦苦经营到上千好友的账号&#xff0c;第二天登录&#xff0c;提示“您的账号受到限制&#xff0c;暂时无法使用”。 例如1&#xff1a; 例如2&#xff1a; Linkedin如何避免封号&#xff1f; 大家肯定不愿看到这…

【JavaSE】Comparable接口和Comparator接口

文章目录一、Comparable接口二、Comparator接口一、Comparable接口 当我们要比较两个类的大小时&#xff0c;我们该如何比较呢&#xff0c;是这样吗&#xff1f; 这样写是错误的&#xff0c;所以我们需要用到接口Comparable的CompareTo方法。 而ComparaTo是抽象方法&#x…

74cms骑士人才招聘系统源码SE版 v3.16.0

介绍&#xff1a; 74cms骑士人才招聘系统是一项基于PHPMYSQL为核心开发的一套专业人才招聘系统。骑士人才系统拥有十多年的人才招聘系统运营解决方案&#xff0c;同时我们提供智能化招聘系统、招考系统等全方位系统化解决方案。 74cms骑士人才招聘系统SE版&#xff1a; 更懂运…

Redhat(9)-磁盘分区-parted-swap-lvm-stratis-vdo-tuned

1.parted 2.swap 3.lvm 4.stratis 5.vdo 6.tuned 1.MBR: MASTER BOOT LOADER 逻辑分区&#xff1a;可以直接格式化使用 扩展分区&#xff1a;不可以直接格式化使用 2.GPT分区 1.parted 2.swap 虚拟内存 linux vmmemoryswap Hibernate :内存 3.lvm 3.1实现的功能和优点&…

前三强重磅揭晓!华秋第八届硬创大赛-全国总决赛路演活动成功举办!

11月19日&#xff0c;华秋第八届硬创大赛-全国总决赛路演活动在深圳高交会成功举办。此次项目路演活动是在深圳市福田区科技创新局指导下&#xff0c;由深圳华秋电子有限公司主办&#xff0c;深圳高交会联合主办的硬件创新领域专业赛事。共13个硬科技领域的优秀项目从众多报名项…

零基础入门JavaWeb——Web基本概念

一、服务器和客户端的概念 1.1 客户端的作用 与用户进行交互&#xff0c;用于接收用户的输入、展示服务器端的数据以及向服务器端传递数据。 1.2 服务器的作用 与客户端进行交互&#xff0c;接收客户端的数据、处理具体的业务逻辑、传递给客户端需要的数据。 1.3 什么是服务…

王道考研——操作系统(第二章 进程管理)

一、进程的概念、组成、特征 进程的概念 进程的组成——PCB 进程的组成——程序段、数据段 知识滚雪球&#xff1a;程序是如何运行的&#xff1f; 进程的组成 进程的特征 知识回顾与重要考点 二、进程的状态与转换 进程的状态——创建态、就绪态 进程的状态——运行态 进程的…