并查集和哈希表的实现

并查集和哈希表的实现

在这里插入图片描述

文章目录

  • 并查集和哈希表的实现
    • 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.kler.cn/a/5113.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python】线程

目录 1. 线程的创建与销毁 2. 线程共享全局变量 3. 互斥锁 4. 进程和线程的对比 1. 线程的创建与销毁 线程是进程的一个分支&#xff0c;进程默认有一个线程&#xff0c;但也可以有多个线程 线程是CPU调度的基本单位 线程是依附在进程里面的&#xff0c;由进程创建&#xff…

java企业级信息系统开发学习笔记02初探spring——利用组件注解符精简spring配置文件

文章目录一、学习目标二、打开01的项目三、利用组件注解符精简spring配置文件&#xff08;一&#xff09;创建新包&#xff0c;复制四个类&#xff08;二&#xff09;修改杀龙任务类&#xff08;三&#xff09;修改救美任务类&#xff08;四&#xff09;修改勇敢骑士类&#xf…

第14章_MySQL事务日志

# 第15章_锁 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司…

2009年9月Java全国计算机等级考试二级笔试试卷

&#xff08;1&#xff09;下列数据结构中&#xff0c;属于非线性结构的是 A&#xff09;循环队列 B&#xff09;带链队列 C&#xff09;二叉树 D&#xff09;带链栈 &#xff08;2&#xff09;下列数据结构中&#xff0c;能够按照“先进后出”原则存取数据的是 A&#xff09;…

可用的公开 RTSP/ RTMP 在线视频流资源地址(亲测可行)

可用的公开 RTSP/ RTMP 在线视频流资源地址(亲测可行) 时间节点&#xff1a;2023/01/23 rtsp&#xff1a; rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 rtmp&#xff1a; 韩国GOOD TV——rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp 伊拉克 A…

python去掉字符串中的指定字符的方法

我们在使用 Python处理字符串的时候&#xff0c;经常会遇到一些字符串中出现了指定字符&#xff0c;比如以下代码&#xff1a; 上面代码中的#就是一个指定字符&#xff0c;在 python中&#xff0c;如果使用#替换为指定字符&#xff0c;那么就会报错。当我们对需要处理的字符进行…

蓝桥杯刷题冲刺 | 倒计时9天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录1.阶乘之和2.生活大爆炸版石头剪刀布1.阶乘之和 题目 链接&#xff1a; 阶乘之和 - 蓝桥云课 (l…

企业安全现状与未来趋势如何?

随着越来越多的第三方办公应用在企业的应用&#xff0c;以及越来越多的企业开始通过云计算、大数据、物联网等数字技术进行数字化转型&#xff0c;一些大企业几乎每秒就能产生上万条数据&#xff0c;这些数据都是企业的资产。 但是&#xff0c;在各种办公软件应用、数字技术给企…

软件测试方法下篇(正交法、场景设计法、错误猜测法)

一、正交法 研究多因素、多水平的一种实验方法&#xff0c;通过正交性找出实验中各因素最优的水平组合&#xff0c;通过分析这些最优组合的实验结果&#xff0c;来分析整个实验的结果与情况。 1、概念及计算方法 因素&#xff1a;待考察的变量 &#xff1b;因素数&#xff1…

Go工程基础知识

Go工程基础前言一、Go 包管理1、技术演进2、go mod二、Go测试1、单元测试2、测试框架3、基准测试a.基本概念b.编写基准测试函数c.运行基准测试函数d.基准测试样例演示三、编码规范四、性能优化与实战1、常见性能优化2、性能调优实战总结前言 记录Go工程基础知识。 一、Go 包管…

论文阅读 | End-to-End Learning of Representations for Asynchronous Event-Based Data

前言&#xff1a;CVPR2019事件表征方面论文 代码&#xff1a;【here】 End-to-End Learning of Representations for Asynchronous Event-Based Data 前言 处理基于事件视觉任务的方式一般分两种&#xff0c;一种是应用可以异步更新的连续模型&#xff0c;另一种是将事件累积…

Python轻量级Web框架Flask(2)——Flask模板渲染/Flask项目拆分

1、run启动参数详解&#xff1a; debug 是否开启调试模式&#xff0c;开启后&#xff08;debug true&#xff09;修改过python代码会自动重启&#xff0c;不用停止运行之后再去启动。port 启动指定服务器的端口号&#xff0c;默认是5000。host 主机&#xff0c;默认是127.0.0…

Java设计模式-7、装饰器模式

装饰器模式 装饰器模式主要对现有的类对象进⾏包裹和封装&#xff0c;以期望在不改变类对象 及其类定义的情况下&#xff0c;为对象添加额外功能。是⼀种对象结构型模式。需 要注意的是&#xff0c;该过程是通过调⽤被包裹之后的对象完成功能添加的&#xff0c;⽽不 是直接修改…

【SSM】MyBatis(十一.MyBatis的高级映射和延迟加载)

文章目录1.准备数据2.多对一2.1 方法一&#xff1a;级联属性映射2.2 方法二&#xff1a;association2.3 方法三&#xff1a;分步查询2.4 一对多延迟加载3. 一对多3.1 方法一&#xff1a;collection3.2 方法二&#xff1a;分步查询1.准备数据 2.多对一 主表和副表 多对一&#…

设计模式——装饰者模式

1 现状 现有已经在线上运行的“吃鸭”业务代码&#xff0c;先声明Eat接口&#xff0c;再用EatDuck类实现Eat接口&#xff0c;最后通过Client初始化EatDuck实例&#xff0c;再调用该实例的eatFood方法实现吃鸭。 1.1 定义吃的接口 package com.design.patterns.adapter;public…

satellite.js库下载、介绍、安装、引用,返回函数的方法

satellite.js是一个js函数库,利用TLE可以推算出来卫星传播的参数方法等,他提供SGP4/SDP4计算所需的函数,还提供坐标转换的函数。 下载: https://github.com/shashwatak/satellite-js https://www.bootcdn.cn/satellite.js/ 安装: npm install satellite.js 或者 yarn add…

chatgpt-retrieval-plugin:chatgpt检索插件简介

文章目录chatgpt检索插件简介加入等待名单介绍目录描述关于插件API检索插件内存功能安全API终端接口快速启动扩展阅读TIPS1:bearer_tokenchatgpt检索插件简介 引自官方&#xff1a;项目git地址 ChatGPT检索插件允许您通过用日常语言提问来轻松搜索和查找个人或工作文档。 加…

linux串口通信

linux下串口通信与管理 linux下的串口与windows有一些区别&#xff0c;下面将介绍一下linux下串口通信管理 查看是否支持USB串口&#xff1a; #lsmod | grep usbserial     如果没有信息&#xff1a;sudo apt-get install setserial 插上USB转串口&#xff0c;在终端输入…

硬件语言Verilog HDL牛客刷题day04 序列检测部分

1.VL25 输入序列连续的序列检测 1.题目&#xff1a; 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足01110001序列&#xff0c;当信号满足该序列&#xff0c;给出指示信号match。 模块的接口信号图如下&#xff1a; 2.解题思路 2. 1 首先 暴力的手段&#xff0c; …

线程安全、线程同步(同步代码块、同步方法、同步锁)

一. 线程安全 1.1 线程安全问题是什么&#xff0c;发生的原因 多个线程同时修改同一共享资源的时候&#xff0c;会出现线程安全问题。读数据是绝对不会出现线程安全问题的&#xff0c;它一定是因为同时在修改。一旦线程同步了&#xff0c;就是解决了安全问题了。CPU负责调度线…
最新文章