并查集详解编程语言
编程语言 详解 查集
2023-06-13 09:11:53 时间
题目:给定N个人物和M组朋友关系,计算出他们之间形成多少个朋友圈
举个例子,比如现在有5个宠物,分别是小猫1,小猫2,小猫3,小狗1,小狗2。再告诉你小猫1和小狗1是好朋友,小猫2和小狗1是好朋友,小猫3和小狗2是好朋友。这样它们之间就形成了2个朋友圈。如下图:
分析:典型的图结构的数据结构,也可用树结构进行处理
初始时每个节点单独是一棵树,一对好友关系就是把两棵树的树根连接起来
如果1和3是好朋友,并不是连接1和3,而是去找1的根和3的根,发现他们都是2,所以他们本来就在一个朋友圈,不需要相连。
树同样可以用数组存,初始时刻数组中都是-1,当有两个节点需要合并时,只需要将其中一个数的根的值设为另一个数的根的下标就行。
这种结构也叫【并查集】
JAVA:
import java.io.*; import java.util.*; class test public static void main (String[] args) throws java.lang.Exception UnionFindSet ufs = new UnionFindSet(10); ufs.union(0, 1); ufs.union(0, 2); ufs.union(3, 4); ufs.union(3, 1); ufs.union(5, 7); ufs.union(7, 8); ufs.union(7, 8); ufs.print(); System.out.println(ufs.count()); class UnionFindSet { // 存储并查集 private int[] elements; // 记录点的使用情况 private int[] flag; UnionFindSet(int n) { // 初始都为-1 elements = new int[n]; flag = new int[n]; for (int i = 0; i i++) { elements[i] = -1; flag[i] = 0; // 找到一个数的根 public int find(int x) { while(elements[x] != -1) { x = elements[x]; return x; // 把两个数的根连起来 public void union(int x, int y) { flag[x]++; flag[y]++; // x的根 int rootx = find(x); // y的根 int rooty = find(y); // 如果不是同一个根就连起来 if(rootx != rooty) { elements[rootx] = rooty; // 计算形成了多少颗树 public int count() { int count = 0; for(int i=0; i elements.length; i++) { if(elements[i] == -1 flag[i]!=0) { count++; return count; // 打印并查集 public void print() { for(int i=0; i elements.length; i++) { if(flag[i]!=0){ System.out.print(elements[i] + " "); }else{ System.out.print(" * "); System.out.println(); }
输出(* 表示没有形成朋友圈的单独点):
1 2 -1 4 2 7 * 8 -1 * 2优化一:【基于树高度的合并优化】
特殊情况:1和2是好朋友,2和3是好朋友,3和4是好朋友,4和5是好朋友
这种情况树就退化成链表,所以树的合并这里可以优化一下,矮的树向高的树合并,尽量避免树的高度无限制的增长
JAVA:
import java.io.*; import java.util.*; class test public static void main (String[] args) throws java.lang.Exception UnionFindSetMergeOptimize ufsmo = new UnionFindSetMergeOptimize(10); ufsmo.union(0, 1); ufsmo.union(1, 2); ufsmo.union(2, 3); ufsmo.union(3, 4); ufsmo.union(4, 5); ufsmo.union(5, 6); ufsmo.union(6, 7); ufsmo.union(7, 8); ufsmo.union(8, 9); ufsmo.print(); System.out.println(ufsmo.count()); class UnionFindSetMergeOptimize { // 存储并查集 private int[] elements; // 存储树的高度 private int[] heights; // 记录点的使用情况 private int[] flag; UnionFindSetMergeOptimize(int n) { elements = new int[n]; heights = new int[n]; flag = new int[n]; for (int i = 0; i i++) { // 初始都为-1 elements[i] = -1; // 初始高度1 heights[i] = 1; flag[i] = 0; // 找到一个数的根 public int find(int x) { while(elements[x] != -1) { x = elements[x]; return x; // 把两个数的根连起来 public void union(int x, int y) { flag[x]++; flag[y]++; // x的根 int rootx = find(x); // y的根 int rooty = find(y); // 如果不是同一个根就连起来 if(rootx != rooty) { // 矮树向高树合并 if(heights[rootx] heights[rooty]) { elements[rooty] = rootx; } else if(heights[rootx] heights[rooty]) { elements[rootx] = rooty; } else { // 如果高度相同,随便合并 elements[rootx] = rooty; // 但是记得合并后高度加一 heights[rooty]++; // 打印并查集 public void print() { for(int i=0; i elements.length; i++) { if(flag[i]!=0){ System.out.print(elements[i] + " "); }else{ System.out.print(" * "); System.out.println(); for(int i=0; i heights.length; i++) { System.out.print(heights[i] + " "); System.out.println(); // 计算形成了多少颗树 public int count() { int count = 0; for(int i=0; i elements.length; i++) { if(elements[i] == -1 flag[i]!=0) { count++; return count; }
输出:
1 -1 1 1 1 1 1 1 1 1 //树的集合 1 2 1 1 1 1 1 1 1 1 //每个节点的高度 1优化二:【路径压缩优化】
每次查找都会顺藤摸瓜的找,直到找到值为-1的根节点为止,与其这样不如直接让这个节点指向根节点。不过这样的话,树的高度会发生变化,基于树的高度优化就会不准了
JAVA:
import java.io.*; import java.util.*; class test public static void main (String[] args) throws java.lang.Exception UnionFindSetPathOptimize ufspo = new UnionFindSetPathOptimize(10); ufspo.union(0, 1); ufspo.union(1, 2); ufspo.union(2, 3); ufspo.union(3, 4); ufspo.union(4, 5); ufspo.union(5, 6); ufspo.union(6, 7); ufspo.union(7, 8); ufspo.union(0, 9); ufspo.print(); System.out.println(ufspo.count()); class UnionFindSetPathOptimize { // 存储并查集 private int[] elements; // 记录点的使用情况 private int[] flag; UnionFindSetPathOptimize(int n) { // 初始都为-1 elements = new int[n]; flag = new int[n]; for (int i = 0; i i++) { elements[i] = -1; flag[i] = 0; // 找到一个数的根 public int find(int x) { // 保存原始x值 int originX = x; // 找到根 while(elements[x] != -1) { x = elements[x]; // 把这一路的节点全部直接连到根上 while(originX != x) { int tempX = originX; originX = elements[originX]; elements[tempX] = x; return x; // 把两个数的根连起来 public void union(int x, int y) { flag[x]++; flag[y]++; // x的根 int rootx = find(x); // y的根 int rooty = find(y); // 如果不是同一个根就连起来 if(rootx != rooty) { elements[rootx] = rooty; // 计算形成了多少颗树 public int count() { int count = 0; for(int i=0; i elements.length; i++) { if(elements[i] == -1 flag[i]!=0) { count++; return count; // 打印并查集 public void print() { for(int i=0; i elements.length; i++) { if(flag[i]!=0){ System.out.print(elements[i] + " "); }else{ System.out.print(" * "); System.out.println(); }
输出:
8 8 8 8 8 8 8 8 9 -1 1
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20727.html
cjava相关文章
- python通过正则表达式分析网页中的图片并进行替换详解编程语言
- JS实现跑马灯效果(向左,向上)详解编程语言
- 一种求离散数学传递闭包的算法java实现详解编程语言
- Java实现DES加密解密代码详解编程语言
- java nio文件传输例子详解编程语言
- Java清除字符串中重复出现的字符详解编程语言
- 使用FileObserver 类监听android sd卡变动详解编程语言
- python入门(八):连接mysql和STMP详解编程语言
- EL中界说函数详解编程语言
- C++String 类完整源代码详解编程语言
- 微软的.NET Core开始支持Raspberry Pi 3详解编程语言
- 使用html+css+js实现日历与定时器,看看今天的日期和今天剩余的时间。详解编程语言
- Myexclipse创建Junit测试详解编程语言
- JS面向对象例子详解编程语言
- vue操作的填坑之旅详解编程语言
- java关键字之static详细学习详解编程语言
- 文件上传之后,并且对tomcat的设置问题记录详解编程语言
- Java Date类的使用总结详解编程语言
- Java关闭Socket来终止线程详解编程语言
- springboot添加多数据源连接池并配置Mybatis详解编程语言
- C语言_结构体变量指针做函数参数的使用案例详解编程语言
- 对多个有序数组,实现归并操作详解编程语言
- S4 HANA BP-客商共用编码处理(示例:已存在的供应商编码扩展客户数据)详解编程语言
- 自建屏幕字段双击功能1详解编程语言
- Explain About The PO Confirmation Control Key In Detail–采购订单中的确认控制详解编程语言
- SAP各种凭证的冲销详解编程语言
- abap 中 FIELD-SYMBOLS的使用方法详解编程语言
- 利用python计算windows全盘文件md5值的脚本详解编程语言