当前位置:   article > 正文

并查集是什么?怎么模拟实现?如何应用?

并查集

目录

一、什么是并查集?

二、并查集可以解决哪些问题?

三、并查集的模拟实现

3.1、并查集的定义

3.2、查询两个元素是否是同一个集合

3.3、合并两个集合

3.4、求集合个数

3.5、并查集完整代码

小结


一、什么是并查集

        我们可以想象这样一个过程,开始时有n个元素,某些元素开始和其他元素按照一定规律进行集合合并,有可能就会分成几个集合。在这个过程中要反复某个元素是哪个集合的,这样的运算,被抽象成数据类型叫做“并查集”;

还是不太理解?举个例子

        例如:现在有10个人,分别对其进行编号,下标都为-1(为什么是-1,后面会解释),如下图:

现在将这个一个个零散人组建成如下三个团体:

 他们的下标被修改为:

 解释:

1. 数组的下标对应集合中元素的编号

2. 数组中如果为负数,负号表示当前下标为根结点,数字代表该集合中元素个数

3. 数组中如果为非负数,他的下标代表该元素的双亲结点

若继续合并,分成了如下两个小团体,数组变化如下:


二、并查集可以解决哪些问题?

通过以上栗子,可以发现,并查集可以解决以下问题:

1.查找元素属于哪个集合

方法:通过树一直找到根,也就是数组中的负数

2.查看两个元素是否属于同一个集合

方法:通过树一直找到根,若根相同说明是同一集合;

3.将两个集合合并成一个集合

方法:将一个集合名称改成另一个集合名称;

4.求集合个数

方法:数组中元素为负数是当前下标集合的个数;


三、并查集的模拟实现

3.1、并查集的定义

底层是一个数组,并且全部初始化为-1;

  1. public class UnionFindSet {
  2. public int[] elem;
  3. public UnionFindSet(int n) {
  4. this.elem = new int[n];
  5. Arrays.fill(elem, -1);
  6. }
  7. }

3.2、查询两个元素是否是同一个集合

这里实现很简单,只需要比较两个元素的根节点是否相同即可,那么就需要先找到两个根节点,可以通过一个循环进行查找,当查找到的元素小于0时,说明找到根节点了;

  1. /**
  2. * 查找数组 x下标是否是根节点
  3. * @param x
  4. * @return
  5. */
  6. public int findRoot(int x) {
  7. if(x < 0) {
  8. throw new IndexOutOfBoundsException("下表不合法,是负数");
  9. }
  10. while(elem[x] >= 0) {
  11. x = elem[x];
  12. }
  13. return x;
  14. }
  15. /**
  16. * 查询 x1 和 x2 是不是同一个集合
  17. * @param x1
  18. * @param x2
  19. * @return
  20. */
  21. public boolean isSameUnionFindSet(int x1, int x2) {
  22. int index1 = findRoot(x1);
  23. int index2 = findRoot(x2);
  24. return index1 == index2;//跟结点是否相同
  25. }

3.3、合并两个集合

这里首先需要找到需要合并的两个元素的根节点,只有根节点不同才能进行合并,怎么合并呢?就是修改两个值,元素一的值因该被修改为这两个元素的和(这是更新孩子的数量),元素二的值因该被修改为元素一的下标(这是跟新另一颗树的根结点)(这里也很简单,建议参照上面我画的图去对比);

  1. /**
  2. * 合并操作
  3. * @param x1
  4. * @param x2
  5. */
  6. public void union(int x1, int x2) throws Exception {
  7. //先找到 x1, x2 的根节点
  8. int index1 = findRoot(x1);
  9. int index2 = findRoot(x2);
  10. if(index1 == index2) {
  11. return;
  12. }
  13. elem[index1] = elem[index1] + elem[index2];
  14. elem[index2] = index1;
  15. }

3.4、求集合个数

遍历数组,并用一个计数器记录负数的个数,就是集合的个数;

  1. /**
  2. * 集合个数
  3. * @return
  4. */
  5. public int getCount() {
  6. int count = 0;
  7. for(int x : elem) {
  8. if(x < 0) {
  9. count++;
  10. }
  11. }
  12. return count;
  13. }

3.5、并查集完整代码

  1. import java.util.Arrays;
  2. public class UnionFindSet {
  3. public int[] elem;
  4. public UnionFindSet(int n) {
  5. this.elem = new int[n];
  6. Arrays.fill(elem, -1);
  7. }
  8. /**
  9. * 查找数组 x下标是否是根节点
  10. * @param x
  11. * @return
  12. */
  13. public int findRoot(int x) {
  14. if(x < 0) {
  15. throw new IndexOutOfBoundsException("下表不合法,是负数");
  16. }
  17. while(elem[x] >= 0) {
  18. x = elem[x];
  19. }
  20. return x;
  21. }
  22. /**
  23. * 查询 x1 和 x2 是不是同一个集合
  24. * @param x1
  25. * @param x2
  26. * @return
  27. */
  28. public boolean isSameUnionFindSet(int x1, int x2) {
  29. int index1 = findRoot(x1);
  30. int index2 = findRoot(x2);
  31. return index1 == index2;//跟结点是否相同
  32. }
  33. /**
  34. * 合并操作
  35. * @param x1
  36. * @param x2
  37. */
  38. public void union(int x1, int x2) {
  39. //先找到 x1, x2 的根节点
  40. int index1 = findRoot(x1);
  41. int index2 = findRoot(x2);
  42. if(index1 == index2) {
  43. return;
  44. }
  45. elem[index1] = elem[index1] + elem[index2];
  46. elem[index2] = index1;
  47. }
  48. /**
  49. * 集合个数
  50. * @return
  51. */
  52. public int getCount() {
  53. int count = 0;
  54. for(int x : elem) {
  55. if(x < 0) {
  56. count++;
  57. }
  58. }
  59. return count;
  60. }
  61. public void print() {
  62. for(int x : elem) {
  63. System.out.print(x + " ");
  64. }
  65. System.out.println();
  66. }
  67. /**
  68. * 测试
  69. * @param args
  70. */
  71. public static void main(String[] args) {
  72. UnionFindSet unionFindSet = new UnionFindSet(10);
  73. System.out.println("合并:0 和 6:");
  74. unionFindSet.union(0, 6);
  75. System.out.println("合并:0 和 7:");
  76. unionFindSet.union(0, 7);
  77. System.out.println("合并:0 和 8:");
  78. unionFindSet.union(0, 8);
  79. System.out.println("合并:1 和 4:");
  80. unionFindSet.union(1, 4);
  81. System.out.println("合并:1 和 9:");
  82. unionFindSet.union(1, 9);
  83. System.out.println("合并:2 和 3:");
  84. unionFindSet.union(2, 3);
  85. System.out.println("合并:2 和 5:");
  86. unionFindSet.union(2, 5);
  87. unionFindSet.print();
  88. System.out.println("合并:8 和 1:");
  89. unionFindSet.union(8, 1);
  90. unionFindSet.print();
  91. System.out.println(unionFindSet.isSameUnionFindSet(6, 9));
  92. System.out.println(unionFindSet.isSameUnionFindSet(8, 2));
  93. }
  94. }

小结

面试考的少,自行斟酌~


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/945642
推荐阅读
相关标签
  

闽ICP备14008679号