并查集和哈希表的实现

news2024/10/7 12:17:23

并查集和哈希表的实现

在这里插入图片描述

文章目录

  • 并查集和哈希表的实现
    • 1.并查集的功能
    • 2.并查集的基本原理
    • 3.并查集的实现
  • 哈希表(hash)
    • 1.拉链法
    • 2.开放寻址法
      • 方法流程
      • 代码演示
    • 3,字符串哈希


1.并查集的功能

1.将两个集合进行合并

2.询问两个元素是否在一个集合里面

2.并查集的基本原理

基本原理:每一个集合用一棵树来表示,树根的编号就是整个集合的编号,每一个点存储的都是其父节点,p[x]表示的就是x的父节点

要考虑的问题有三个
问题一:如何判断树根 if(p[x]==x)

问题二:如何求x的集合编号 while(p[x]!=x) x=p[x]

问题三:如何合并两个结合 p[x]是x的集合编号,p[y]是y的集合编号,p[x]=y;表示x的父节点是y

3.并查集的实现

并查集,查找两个元素,是否在一个集合里面,而且可以将两个集合合并

//首先要设置全员变量
using namespace std;

const int n = 100010;
int n,m;
int p[N];//p[N]来存储的是父节点

//寻找操作
int find (int x)
{
    if(p[x]!=x) p[x]=find (p[x]);
	return p[x];//如果x的父节点不是x的时候,就要将p[x]放在find函数里面去,用p[x]来接收,直到最后p[x]==x ,递归返回之后,这个结合的所有结点的父节点都是x
}
//相当于 返回x的祖宗结点,并且有路径压缩

//如果说我们输入”M“来表示将两个集合合并
int main()
{
    scanf("%d%d",&n,&m);
    //输入n个数字,进行m次操作
    for(int i = 1;i <= n; i++ ){
        p[i]=i;//首先让n个结点的父节点都是自己本身,因为初始的时候每一个结点就是一个集合
    }
    
    while(m--){
        char op[2];//输入操作的字符
        int a,b;//指定的两个结点
        scanf("%s%d%d",op,&a,&b);
        
		if(op[0]=='M'){
          p[find(a)]=find(b);//将两个集合合并,可以先找到a,b的祖宗结点,这样讲a祖宗结点的父节点设置为b的祖宗结点,这样两个集合合并
        }
        else {//op不是M的时候,就是要进行判定ab是否是在一个集合里面
            //如果是I 表示判断是否两个结点在一个集合里面
            if(find(a)==find(b)){
                printf("Yes\n");
            }else {
                printf("NO\n");
            }
        }
    }
    return 0;
}

哈希表(hash)

哈希表常用的两个方式来存储数据, 拉链法和开放寻址法

哈希表的原理就是将一个很大范围中的数字,放置在一个小范围的数组中存储起来.

可以实现的功能为1.存储 2.查找

比如-109 ~109 存放在一个范围为0~105 的数组中存储起来

操作流程

1.先进行mod 取模,得到一个数字,存放在一个数组中

2.因为对于这些数据,会有冲突的时候,所以对于多个数据取模得到一个数值,使用拉链法,数组的每一个槽都可以对应一个链表形式的结构,通过这个结构,将取模相同的元素,放在这个槽对应的链表中,然后在查找的过程中近似实现O(1)的操作

我们mod的时候使用的数字应该是个质数,如果处理在0-100000范围内的数据,那么就要找到大于100000的最小质数

查找mod数值的方法为:

int getInteger(int n){//n传递为100000
	while(n++){
        for(int j=2;j*j<=n;j++){
            if(n%j==0){
                break;
            }
        }
    }
    return n;
}

1.拉链法

操作的流程,在c/c++中需要自行用数组来表示链表,在Java中可以使用LinkedList来表示链表

我们使用c++来举例

using namespace std;

const int n=100003;//我们提前找到了这个大于最大范围的最小质数
int h[N],e[N],ne[N],idx;
//h[N]用来存储模的数值
//e[N]用来形成链表,存储的数值
//ne[N]用来得到当前N下标对应的下一个数值
//idx表示链表的下标

//我们要实现的是,输入一个n,表示有n个操作,如果是输入"I x"表示插入数值x,如果是"Q x"实现查找x,查找到x 输出Yes 没有输出No
#include<string.h>
#include<stdio.h>
#include<iostream>

void insert(int x){
    int k=(x % N + N ) % N;//取模,x%N可能为负值,所以+N,然后再mod N
    //得到k就是要存储在槽中的数值h[N],就是找到位置
    e[idx]=x;//用当前下标idx存储数值为x
    ne[idx]=h[k];//然后idx的下一个坐标为h[k]的位置也就是说,头插
    h[k]=idx;//然后h[k]=idx h[k]就相当于头节点
    idx++;//添加一个元素idx++;
    
}

bool find(int x){
    int k=( x % N + N ) % N ;
	for(int i=h[k];i!=-1;i=ne[i]){
        //得到x的存储在槽终点 位置h[k],然后查询链表
        if(x==e[i]){
            return true;
        }
    }
    return false;
}

int main()
{
    int n;
    scanf("%d",&n);
    //表示输入n个操作
    memset(h,-1,sizeof(h));//对于h[N]数组进行赋值为-1
    
    while(n--){
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        if(op=='I'){//插入存储数值x
            insert(x);
        }else {
            //进行查找x
            if(find(x)){
                printf("Yes\N");
            }else{
                printf("No\n");
            }
        }
    }
    return 0;
}

拉链法的主要内容是

1.得到k的数值,然后在h[k]数组上加上链表,使用e[idx]数组来存储数值x,用ne[idx]=h[k],h[k]=idx来实现头插,最后idx++;

//插入的算法
void insert(int x){
    int k=(x % N + N) % N;
    e[idx]==x;
    ne[idx]=h[k];
    h[k]=idx;
    idx++;
}

2.我们对于h[N]数组初始化的时候赋值为-1,但是在插入数据的时候,会头插,将idx的数值赋值给了h[N]数组

//查询的时候的代码
bool find(int x){
    int k=(x % N + N) % N;    
    for(int i=h[k];i!=-1;i=ne[i]){
        if(e[i]==x){
            return true;
        }
    }
    return false;
}

拉链法主要是插入函数insert和find函数.

2.开放寻址法

开放寻址法的主要方式为: 开辟的数组的发小是输入数据范围的2,3倍

方法流程

1.先找到一个合适的质数

2.插入方法为,在存储数据的时候通过hash函数,得到k,然后存储

3.进行查找

代码演示

using namespace std;

const int N=100003;
int null=0x3f3f3f3f;//设定一个极大值
int h[N];//存储数值的数组

int find(int x){
    int k=(x % N + N) % N;
    while(h[k]!=null && h[k]!=x){
        //如果说是不为null不为x,表示没有存储过这个数值
        k++;//没找到就k++
        if(k==N)  k=0;//如果等于N,那就从头0开始
    }
    return k;//找到了就返回的k值
}

int main()
{
    int n;//表示n个操作数
    scanf("%d",&n);
    memset(h,0x3f,sizeof(h));
    //memset 作用是对于一个地址进行赋值,可以指定大小,指定数值
    //函数有三个参数
    //第一个是 输入地址,比如h表示数组地址,第二个参数是存放数值,这个是存放一个字节的数值,如果是int 数组,那就是4个0x3f
    //第三个参数是数组大小,也就是要赋值的范围
 
    while(n--){
        char op[2];
        int x;
        scanf("%s%d",op,&x);
     	int k= find(x);
        if(op[0]=='I'){
            h[k]=x;//进行插入数值x
        }else{
            //判断是否有x在数组里面
            if(h[k]!=null) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

3,字符串哈希

类似于kmp 但是可以实现查找字符串到O(1),基本上更好

字符串hash的用法步骤是:

1.使用p进制,类似于10进制这样,用h[0],h[1]….来存储前n个字符串的p计数

2.使得一串字符,得到一个数字 p进制,有个经验值为131或者是13331;如果是abcd 那就是(a *p[3]+b*p[2]+c*p[1]+d*p[0]) % Q Q也有经验值 为264 因为用h数组存储哈希值,264 可以用unsigned long long来表示,如果大于264 会超出存储范围 ,自动模值

那就是直接 unsigned long long h[n]=a*p[3]+b*p[2]+c*p[1]+d*p[0] 即可

3.然后如果是想要得到 l 到 r 之间的字符串哈希,前段为高位,后端为低位,如果是r>1 那么就是让 h [ l-1 ] *p[ r- l +1 ] 然后h[r]-h[l-1]*p[r-l+1]

代码如下

using namespace std;

typedef unsigned long long ULL;
const int N=100010,p=131;

int n,m;
char str[N];
ULL h[N],p[N];

//题目要求  第一行输入n m str[N]  表示为 n为字符串长度 m为比较次数 str为字符串数组
//接下来m行  输入l1 r1 l2 r2  比较 l1到r1 l2到r2  这两段字符串是否相等

ULL get(int l,int r){
    return h[r]-h[l-1]*p1[r-l+1];
}
int main()
{
	scanf("%d%d%s", &n, &m, str + 1);
	//str数组从1开始 
	//
	p[0] = 1;
	//得到数字
	for (int i = 1; i <= n; i++)
	{
		p[i] = p1[i - 1] * p;
		h[i] = h[i - 1] * p + str[i];
	}

	while (m--)
	{
		int l1, r1, l2, r2;
		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);

		if (get(l1, r1) == get(l2, r2))  						printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

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

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

相关文章

js中exec与match的区别

const regex1 RegExp(f(o.?),g); const str1 table foatball, fobsball; let array1; let array2; array1 regex1.exec(str1) array2 str1.match(regex1)console.log(array1, array1); console.log(array2, array2); //没有g的情况下,都是找到第一个匹配,并且如果有分组,…

SPSS数据分析--假设检验的两种原假设取舍决定方式

假设检验的两种原假设取舍决定方式 在t检验&#xff0c;相关分析&#xff0c;回归分析&#xff0c;方差分析&#xff0c;卡方检验等等分析方法中&#xff0c;都需要用到假设检验。假设检验的步骤一般如下&#xff1a; 提出假设&#xff1a;H0 vs H1 ;假设原假设H0 成立的情况…

Java课题笔记~ 动态SQL详解

一、动态 sql 是什么&#xff1f; 1、动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中&#xff0c;开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。 例如&#xff0c;拼接时要确保添加了必要的空格&#xff0c;还要…

AWS多账户单点登录 IAM Identity Center(AWS SSO)

需求场景 多个aws账户&#xff0c;登陆麻烦且不安全&#xff0c;SSO单点功能并且外部身份提供者 — 如果您要管理外部身份提供者&#xff08;IdP&#xff09;&#xff08;例如 Okta 或 Active Directory&#xff09;中的用户。 官方文档&#xff1a;https://docs.aws.amazon.c…

Maven引入本地jar包

maven做为一种强大的依赖管理工具&#xff0c;可以帮助我们更方便的管理项目中的依赖&#xff1b;而在使用过程中我们难免会有需要引入本地jar包的需求&#xff0c;这里踩过坑之后我分享俩种引入方式&#xff1b; 1.上传jar到本地maven仓库&#xff0c;再引入 使用此方法后可…

基于Translators的多语言翻译解决方案

当Translators库,一个用Python为个人和学生提供免费、多样、愉快翻译的库。 文章目录 Translators支持的翻译服务安装与入门参数和功能支持的语言调试和运行环境API服务Translators Translators库是一个强大的Python库,旨在为个人和学生提供免费、多样、愉快的翻译体验。它支…

文件传输协议FTP与托管文件传输MFT有什么区别?

传输敏感数据是日常业务中不可或缺的一环。但是&#xff0c;在把敏感数据从A点搬到B点的过程中&#xff0c;保证该敏感数据的安全是组织的重要任务&#xff0c;因此最好选择一种能够确保文件安全的方案。 FTP与MFT有什么不同&#xff1f; FTP&#xff08;文件传输协议&#xf…

考研408 | 【计算机网络】概述

计算机网络体系结构 计算机网络概述&#xff1a;1.概念&#xff0c;组成&#xff0c;功能&#xff0c;分类2.标准化工作及相关组织3.性能指标体系结构&参考模型&#xff1a;1.分层结构2.协议&#xff0c;接口&#xff0c;服务3.ISO/OSI模型4.TCP/IP模型 目录 计算机网络体…

SpringBoot+AOP+Redission实战分布式锁

文章目录 前言一、Redission是什么&#xff1f;二、使用场景三、代码实战1.项目结构2.类图3.maven依赖4.yml5.config6.annotation7.aop8.model9.service 四、单元测试总结 前言 在集群环境下非单体应用存在的问题&#xff1a;JVM锁只能控制本地资源的访问&#xff0c;无法控制…

AMEYA--大唐恩智浦DNB1168助力CTC和CTP新电池技术价值突破

“里程焦虑”是新能源汽车行业中绕不开的话题。从短期来看&#xff0c;电池材料难有突破性的进展&#xff0c;电池能量密度提升受限。那么&#xff0c;对动力电池进行结构优化、为电芯“腾出”更大的空间&#xff0c;就成为提高汽车续航能力的不二选择。 动力电池的瘦身之路 传…

Stable diffusion从入门到进阶教程

全网最全Stable Diffusion教程&#xff0c;耗时三个月制作。 基础教程 Stable Diffusion知识大纲05-26 10:57 Stable Diffusion安装教程06-09 15:58 抖音资料发放处昨天 14:46 比较好用的 AI绘画网站整理05-26 10:24 Stable Diffusion配置要求05-08 18:01 StableDiffusio…

15 springboot项目——thymeleaf语法与关闭模板引擎

15.1 thymeleaf语法 在html文件中&#xff0c;有些是需要使用本地的css样式&#xff0c;使用thymeleaf语法加载&#xff1a; 首先对head标签上面的html标签进行更改&#xff1a; <html lang"en" xmlns:th"http://www.thymeleaf.org"> 其次&#xff…

2024考研408-计算机网络 第三章-数据链路层学习笔记

文章目录 前言一、数据链路层的功能1.1、数据链路层的研究思想1.2、数据链路层基本概念1.3、数据链路层功能概述&#xff08;5个功能&#xff09; 二 、组帧2.1、封装成帧以及发送帧的过程&#xff08;包含名词解释&#xff09;2.2、实现透明传输及四种组帧方法2.2.1、什么是透…

shell 自动创建磁盘分区 PV 、VG、 LV

设计思路&#xff1a; 1、创建磁盘分区变量集合 devName("vdb" "vdc" "vdd" "vde") 2、创建for循环&#xff0c;磁盘分区数量 /dev/vdb0~3&#xff0c;用于在for循环中if判断磁盘分区是否已经存在 if 判断条件 [[ -b "目录名称…

LCD驱动深入分析

很多人都会说操纵lcd显示就是操纵framebuffer&#xff0c;表面上来看是这样的。实际上是frambuffer就是linux内核驱动申请的一片内存空间&#xff0c;然后lcd内有一片sram&#xff0c;cpu内部有个lcd控制器&#xff0c;它有个单独的dma用来将frambuffer中的数据拷贝到lcd的sram…

腾讯云 +WordPress 搭建个人网站

搭建个人网站 准备服务器配置宝塔面板开放端口权限 部署WordPress访问效果 准备服务器 腾讯云网址 购买腾讯云服务器有新人优惠&#xff0c;第一次购买会比较便宜&#xff08;赚的是你续费的钱&#xff09;&#xff0c;我这里买了轻量的三年是408&#xff0c;还是比较实惠的&a…

端口映射教程vs快解析内网穿透

随着社会信息化的发展&#xff0c;很多人都开始关注网络问题&#xff0c;掌握一些基础的网络知识是非常有必要的。其中&#xff0c;端口映射作为一项重要的技术&#xff0c;在网络通信中起到了至关重要的作用。 端口映射在现实生活中有着广泛的应用。如果你是一位游戏爱好者&a…

Linux--打印到显示器的内容输出重定向到文件里的代码

// ./myproc hello int main(int argc, char* argv[]) {if (argc ! 2){return 2;}int fd open("log.txt", O_WRONLY | O_CREAT | O_TRUNC);if (fd < 0){perror("open");return 1;}dup2(fd, 1);fprintf(stdout, "%s\n", argv[1]); } 运行结果…

人工智能数据获取与数据管理指南

成功部署AI实践典范 人工智能的部署离不开大规模高质量训练数据的注入&#xff0c;大数据的管理制度也随着人工智能的发展而日益完善&#xff0c;数据采集与治理更是企业做AI部署策略时的两大复杂因素。 有几项最佳实践可以作为构建和部署有效的AI解决方案的典范。建立长期全面…

C语言笔试题训练【第一天】

目录 第一题 第二题 第三题 第四题 第五题 大家好&#xff0c;我是纪宁。 从今天开始博主会日更一些经典的C语言笔试题&#xff0c;持续20天左右。题目类型为5道选择题加2道编程题&#xff0c;希望能和大家一起进步。 第一题 1.读程序&#xff0c;下面程序正确的输出是&…