
文章目录
1.将两个集合进行合并
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
并查集,查找两个元素,是否在一个集合里面,而且可以将两个集合合并
//首先要设置全员变量
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;
}
哈希表常用的两个方式来存储数据, 拉链法和开放寻址法
哈希表的原理就是将一个很大范围中的数字,放置在一个小范围的数组中存储起来.
可以实现的功能为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;
}
操作的流程,在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,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;
}
类似于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;
}
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>
查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
假设我有一个在Ruby中看起来像这样的哈希:{:ie0=>"Hi",:ex0=>"Hey",:eg0=>"Howdy",:ie1=>"Hello",:ex1=>"Greetings",:eg1=>"Goodday"}有什么好的方法可以将它变成如下内容:{"0"=>{"ie"=>"Hi","ex"=>"Hey","eg"=>"Howdy"},"1"=>{"ie"=>"Hello","ex"=>"Greetings","eg"=>"Goodday"}} 最佳答案 您要求一个好的方法来做到这一点,所以答案是:一种您或同事可以在六个月后理解
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复