萌新学习算法——并查集基础

2023-05-16

并查集


在算法设计中,将一个集合和另外一个集合合并时,就会用到并查集。假如不用并查集,你可能会用到集合和列表来实现,这样会使代码看起来很复杂,而且执行效率不高,下面用洛谷的题目P3367 并查集.来举例子:

题目描述:

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式
第一行包含两个整数N、M,表示共有N个元素和M个操作。

接下来M行,每行包含三个整数Zi、Xi、Yi

当Zi=1时,将Xi与Yi所在的集合合并

当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N

输出格式
如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N

代码(用集合和列表实现)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

public class Main {
	public static int n, m;
	public static boolean flag[];  //用来判断是否已经加入列表,初始状态false
	//利用ArrayList来存储不同的集合,用set来存储每个集合里面的元素
	public static ArrayList<HashSet<Integer>> als = new ArrayList<>(); 

	public static void union(int x, int y) {
		if(x==y) return ;
		if (!flag[x] && !flag[y]) { //x和y都没有在列表里面,需要将x和y加入列表,并保存到同一个集合
			HashSet<Integer> newset = new HashSet<>();
			newset.add(x);
			newset.add(y);
			flag[x] = true;
			flag[y] = true;
			als.add(newset);
		} else {
			if (flag[x] && flag[y]) {  //当x和y都在列表里面
				int s = 0, e = 0;
				//找出x所在列表的下标
				for (int i = 0; i < als.size(); i++)
					if (als.get(i).contains(x)) { 
						s = i;
						break;
					}
				//找出y所在列表的下标
				for (int i = 0; i < als.size(); i++) {
					if (als.get(i).contains(y)) {
						e = i;
						break;
					}
				}
				//若x和y的下标相同,则证明在同一个集合,不需要合并
				if(e==s)
					return;
				//利用迭代器来实现,对下标大的那个集合遍历,将元素逐一加入下标小的那个集合中
				if (e < s) {	
					Iterator<Integer> set = als.get(s).iterator();
					while (set.hasNext())
						als.get(e).add(set.next());
					//最后将大的那个集合从列表中移除
					als.remove(s);
				} else {
					Iterator<Integer> set = als.get(e).iterator();
					while (set.hasNext())
						als.get(s).add(set.next());
					//最后将大的那个集合从列表中移除
					als.remove(e);
				}
				
			}//若只有一个在列表里面,另外一个没有在列表里面 
			else if (flag[x]) { //若x在列表中				
				int i;
				//找出x所在集合的下标
				for (i = 0; i < als.size(); i++)
					if (als.get(i).contains(x))
						break;
				//将y加入x所在集合
				als.get(i).add(y);
				//并将y加入列表
				flag[y] = true;
			} else {//若y在列表中	
				int i;
				//找出y所在集合的下标
				for (i = 0; i < als.size(); i++)
					if (als.get(i).contains(y))
						break;
				//将x加入y所在集合
				als.get(i).add(x);
				//并将y加入列表
				flag[x] = true;
			}
		}
	}

	//判断是否在同一个集合
	public static boolean inSet(int x, int y) {
		for (int i = 0; i < als.size(); i++)
			if (als.get(i).contains(x)) {
				if(als.get(i).contains(y)) //若x和y在同一个集合里面,则返回true,否则返回false
					return true;
				else
					return false;
				}
		return true;
	}

	public static void main(String[] args) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		String str[] = reader.readLine().split(" ");
		n = Integer.parseInt(str[0]);
		m = Integer.parseInt(str[1]);
		flag = new boolean[n + 1];
		int x, y, z;
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < m; i++) {
			str = reader.readLine().split(" ");
			z = Integer.parseInt(str[0]);
			x = Integer.parseInt(str[1]);
			y = Integer.parseInt(str[2]);
			if (z == 1)   
				union(x, y); 
			else {
				//若x==y 不用集合判断,若x或y不在列表里面,证明它自己作为一个集合
				//只有当flag[x] && flag[y] && inSet(x, y)为真时,才证明是同一个集合
				if (x==y||(flag[x] && flag[y] && inSet(x, y))) 
					sb.append("Y").append("\n");
				else
					sb.append("N").append("\n");
			}
		}
		System.out.println(sb);

	}
}

通过上面的代码,我们会发现特别麻烦,接下来我们引入并查集:这里介绍如何用它实现集合的合并和判断是否在同一个集合:

判断是否在同一个集合:

在这里我们先定义了一个find方法去查找x所在的集合的根节点,思想:保存每个集合的根节点,初始化为为-1(利用小于0来判读是否为根节点,后面的数值用来保存该集合有多少个点),其实就一个树状图,利用x集合保存的时它的父亲节点的父亲的下标,直到出现负数为根节点,再根据根节点判断是否为同一个集合,若根节点相同,则是同一个集合:

代码实现(未压缩的find方法):

public static int find(int x)
	{
		while(data[x]<0) return x;
		return find(data[x]);
	}

在这里有一个提高效率的方法,我们可以在找的同时顺带保存根节点,当下次查找的时候直接找到根节点,不需要每次都递归去寻找。

代码实现(压缩的find方法):

	public static int find(int x)
	{
		while(data[x]<0) return x;
		return data[x]=find(data[x]);
	}

接下来我们就可以利用find函数来实现查找两个数是否属于同一个集合。

work方法:

public static boolean work(int x,int y)
	{
		int a=find(x),b=find(y);
		if(a==b)
			return true;
		return false;		
	}

最后我们利用find方法来实现合并的union方法。

union方法:

	public static void union(int x,int y)
	{
		int a=find(x),b=find(y);
		if(a==b)
			return ;
		data[a]+=data[b];
		data[b]=a;
	}

完整代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
	public static int n,m,z,x,y;
	public static int data[];
	
	public static int find(int x)
	{
		if(data[x]<0) return x;	
		return data[x]=find(data[x]); //返回的是每个元素的根节点
	}
	
	public static void union()
	{
		int a=find(x);
		int b=find(y);
		if(a==b) return ; //若同为一个根,则不需要合并
		data[a]+=data[b];  //因为同为负数,所以可以直接相加
		data[b]=a; //将a作为b的根节点 
	}
	
	public static void main(String[] args) throws IOException {
		BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
		String str[]=reader.readLine().split(" ");
		n=Integer.parseInt(str[0]); m=Integer.parseInt(str[1]);
		data=new int[n+1];
		for(int i=1;i<=n;i++)
			data[i]=-1;
		StringBuffer sb=new StringBuffer();
		for(int i=0;i<m;i++)
		{
			str=reader.readLine().split(" ");
			z=Integer.parseInt(str[0]); x=Integer.parseInt(str[1]);
			y=Integer.parseInt(str[2]);
			if(z==1)
				union();
			else
			{
				if(find(x)==find(y))
					sb.append("Y").append("\n");
				else
					sb.append("N").append("\n");
			}
			
		}
		System.out.println(sb);
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

萌新学习算法——并查集基础 的相关文章

随机推荐

  • C语言初阶小练习(8)——指针(2)

    还是一些指针小练习 xff0c 接上次 C语言小练习 xff08 7 xff09 指针 xff08 1 xff09 一起来看看吧 目录 编写程序输入n个整数 xff0c 查找并删除重复的数字 xff0c 打印结果 查找其中出现了多少个连续数
  • 推荐几个代码自动生成器,神器!!!

    20个代码生成框架 老的代码生成器的地址 xff1a https www cnblogs com skyme archive 2011 12 22 2297592 html 以下是大家推荐的最近很火爆的代码生成器神器 如果有更好的希望大家多
  • linux 安装discuz出现“ mysqli_connect()不支持advice_mysqli_connect ”解决方法

    由于不了解php相关技术 xff0c 所以在安装discuz的时候遇到了很多麻烦 xff0c 记录下 首先 xff0c 我的环境是CentOS6 5 xff0c 在安装discuz的时候需要yum很多东西 yum install php p
  • Ubuntu 22.04 LTS下Miniconda安装+换源(踩坑向)

    1 安装Miniconda 我使用的是Python3 8 xff0c 如果需要去其他对应版本 xff0c 请查看 Miniconda conda documentation 下载 wget https repo anaconda com m
  • FreeBSD修改为国内源

    禁用原来的FreeBSD conf ee etc pkg FreeBSD conf 将 enabled yes 改为 enabled no 保存 ESC 然后 a gt a 即可 创建另外一个 FreeBSD conf mkdir p us
  • 关于51单片机的中断

    1 中断的要求 1 中断源有中断请求 Ask for instructions of the CPU interrupt request source called interrupt source 2 此中断源的中断允许位为1 The i
  • 华为机试_HJ5 进制转换【简单】

    描述 写出一个程序 xff0c 接受一个十六进制的数 xff0c 输出该数值的十进制表示 数据范围 xff1a 保证结果在 1 le n le 2 31 1 1 n 231 1 输入描述 xff1a 输入一个十六进制的数值字符串 输出描述
  • bootstrap实现 — 个人简介

    实现 xff1a bootstrap 效果图 xff1a 源码 xff1a lt DOCTYPE html gt lt html gt lt head gt lt meta charset 61 34 utf 8 34 gt lt titl
  • 计蒜客--T1079--打表+控制输出

    假设有 N 盏灯 xff08 NN为不大于 5000 的正整数 xff09 xff0c 从 1 到 N 按顺序依次编号 xff0c 初始时全部处于开启状态 xff1b 有 M 个人 xff08 M 为不大于 N的正整数 xff09 也从 1
  • Authentication plugin ‘caching_sha2_password‘ 服务端也无法连接问题彻底解决

    在网上搜索了很多的帖子 xff0c 发现描述的都是外部客户端无法登录到mysql上 xff0c 登录上服务器以后连接更改配置的方式 xff0c 但是 xff01 xff01 xff01 xff01 xff01 我现在是服务器连接也报错啊啊啊
  • Hexo分类及标签显示

    Hexo根目录配置 config yml category map Blogs categories Blogs Tech categories Tech Tools categories Tools Other categories Ot
  • IDEA查看历史记录

    方法一 文件内 Ctrl 43 右键 Local History Show History xff0c 显示当前文件的本地修改历史 方法二 一 xff1a 在文件内 xff0c 按 Ctrl 43 Shift 43 A 弹出全部搜索对话框
  • SpringBoot-JPA整合ShardingShpere自定义分布式主键

    分布式主键简介 在分布式环境下 xff0c 由于分库分表导致数据水平拆分后无法使用单表自增主键 xff0c 因此我们需要一种全局唯一id生成策略作为分布式主键 当前有如下解决方案 UUID xff08 Universally Unique
  • Gitlab的安装与配置

    安装开始时 xff0c 需确认服务器最小配置是2核4G xff0c 因为gitlab软件比较大 1 配置yum源 xff1a vim etc yum repos d gitlab repo gitlab name 61 gitlab ce
  • Error creating bean with name ‘org.springframework.aop.aspectj.AspectJPointcutAdvisor#0

    问题 xff1a nested exception is org springframework beans factory BeanCreationException Error creating bean with name 39 or
  • Vue前端项目开发页面(二)

    前端界面开发 开发工具版本 64 vue cli 4 5 13 新建Login vue登陆页 1 在 vue exemples 项目 xff0c 选中components目录右键 New Vue Component xff0c 名称为 Lo
  • SpringBoot整合WebSocket

    概述 HTTP 协议是一种无状态的 无连接的 单向的应用层协议 它采用了请求 响应模型 通信请求只能由客户端发起 xff0c 服务端对请求做出应答处理 WebSocket和HTTP一样 xff0c 都是一种网络通信协议 比起HTTP只能由客
  • SpringBoot整合MybatisPlus使用IPage实现分页

    概述 MybatisPlus 提供了分页的功能 IPage内部原理是基于拦截器 xff0c 但是这个拦截的是方法以及方法中的参数 xff0c 这个也会判断是否是查询操作 如果是查询操作 xff0c 才会进入分页的处理逻辑 进入分页逻辑处理后
  • SpringBoot统一异常处理

    概述 SpringBoot 提供了 64 ControllerAdvice 64 RestControllerAdvice 注解可以实现统一异常处理 xff0c 只需要在定义异常类加上以上注解即可 自定义异常处理 定义统一异常处理 span
  • 萌新学习算法——并查集基础

    并查集 在算法设计中 xff0c 将一个集合和另外一个集合合并时 xff0c 就会用到并查集 假如不用并查集 xff0c 你可能会用到集合和列表来实现 xff0c 这样会使代码看起来很复杂 xff0c 而且执行效率不高 xff0c 下面用洛