1.Union: 查看两个元素之间是否可连接

  • 判断网络中节点连接问题
  • 路径问题

    2.并查集的实现:

    并查集中并不保存实际元素,而是元素与索引进行一一映射

    1. // UF 接口
    2. public interface UF {
    3. int getSize();
    4. // 查看两个元素可连接
    5. boolean isConnected(int p, int q);
    6. void unionElements(int p, int q);
    7. }
    1. // 并查集使用 Tree 实现
    2. public class UnionFind implements UF{
    3. // index 对应元素,存储的值为集合的id
    4. private int[] id;
    5. // 先初始化传入的并查集大小,之后的再说
    6. public UnionFind(int[] arr){
    7. id = new int[arr.length];
    8. for(int i = 0; i < arr.length; i++){
    9. id[i] = i;
    10. }
    11. }
    12. // 获取size
    13. @Override
    14. public int getSize(){
    15. return id.length;
    16. }
    17. // 查看索引对应的集合
    18. public int find(int index){
    19. if (index < 0 || index > id.length) {
    20. throw new IllegalArgumentException("out of index");
    21. }
    22. return id[index];
    23. }
    24. // 查看两个元素可连接
    25. @Override
    26. public boolean isConnected(int p, int q){
    27. return find(p) == find(q);
    28. }
    29. @Override
    30. // 修改其中一个节点的ID
    31. // 之后需要将所有均改为同一ID
    32. public void unionElements(int p, int q){
    33. int pID = find(p);
    34. int qID = find(p);
    35. if(qId == pID){
    36. return;
    37. }
    38. for(int i = 0; i < id.length(); i++){
    39. if(find[i] == qID){
    40. find[i] = pID;
    41. }
    42. }
    43. }
    44. }

    // 这时候 isConnected() 的时间复杂度为O(1)
    // 而 unionElement() 为 O(n)

    3.优化

  • 引入父节点,这样每次就只需要修改父节点对应的上一级 节点

    1. // 注意此时 root 节点对应的上一个父节点就是他本身
    2. public class UnionFind implements UF{
    3. // 存储 索引对应的父节点
    4. private int[] parent;
    5. // 先初始化传入的并查集大小,之后的再说
    6. public UnionFind(int[] arr){
    7. parent = new int[arr.length];
    8. for(int i = 0; i < arr.length; i++){
    9. parent[i] = i;
    10. }
    11. }
    12. // 获取size
    13. @Override
    14. public int getSize(){
    15. return parent.length;
    16. }
    17. // 查看索引对应的集合
    18. public int find(int index){
    19. if (index < 0 || index > id.length) {
    20. throw new IllegalArgumentException("out of index");
    21. }
    22. // 要找到 root ,返回 所属的 root 对应的结合ID
    23. // root 节点的值与自身索引相同
    24. while(index != parent[index]){
    25. index = parent[index];
    26. }
    27. return index;
    28. }
    29. // 查看两个元素可连接
    30. @Override
    31. public boolean isConnected(int p, int q){
    32. return find(p) == find(q);
    33. }
    34. @Override
    35. // 修改其中一个节点的ID
    36. // 之后需要将所有均改为同一ID
    37. public void unionElements(int p, int q){
    38. // 获取父节点
    39. int pParent = find(p);
    40. int qParent = find(q);
    41. // 如果两节点相同
    42. if(pParent == qParent){
    43. return;
    44. }else{
    45. // 其中一个元素的父亲节点变指向另一个元素
    46. parent[p] = qParent;
    47. }
    48. }
    49. }

    // 此时 find() 的 时间复杂度为 O(h)
    // unionElements() 的时间复杂度为 O(h)

  • 使用 size() 进行优化

  • 由于时间复杂度与树的高度有关,所以size() 最好

    1. // 注意此时 root 节点对应的上一个父节点就是他本身
    2. public class UnionFind implements UF{
    3. // 存储 索引对应的父节点
    4. private int[] parent;
    5. private int[] sz;
    6. // 先初始化传入的并查集大小,之后的再说
    7. public UnionFind(int[] arr){
    8. parent = new int[arr.length];
    9. for(int i = 0; i < arr.length; i++){
    10. parent[i] = i;
    11. sz[i] = 1;
    12. }
    13. }
    14. // 获取size
    15. @Override
    16. public int getSize(){
    17. return parent.length;
    18. }
    19. // 查看索引对应的集合
    20. public int find(int index){
    21. if (index < 0 || index > id.length) {
    22. throw new IllegalArgumentException("out of index");
    23. }
    24. // 要找到 root ,返回 所属的 root 对应的结合ID
    25. // root 节点的值与自身索引相同
    26. while(index != parent[index]){
    27. index = parent[index];
    28. }
    29. return index;
    30. }
    31. // 查看两个元素可连接
    32. @Override
    33. public boolean isConnected(int p, int q){
    34. return find(p) == find(q);
    35. }
    36. @Override
    37. // 修改其中一个节点的ID
    38. // 之后需要将所有均改为同一ID
    39. public void unionElements(int p, int q){
    40. // 获取父节点
    41. int pParent = find(p);
    42. int qParent = find(q);
    43. // 如果两节点相同
    44. if(pParent == qParent){
    45. return;
    46. }else{
    47. if(sz[qParent] > sz[pParent]){
    48. sz[qParent] += sz[pParent];
    49. parent[pParent] = qParent;
    50. }else{
    51. sz[pParent] += sz[qParent];
    52. parent[qParent] = pParent;
    53. }
    54. }
    55. }
    56. }
  • 使用 rank()树的高度优化

    1. blic class UnionFind implements UF{
    2. // 存储 索引对应的父节点
    3. private int[] parent;
    4. private int[] rank;
    5. // 先初始化传入的并查集大小,之后的再说
    6. public UnionFind(int[] arr){
    7. parent = new int[arr.length];
    8. rank = new int[arr.length];
    9. for(int i = 0; i < arr.length; i++){
    10. parent[i] = i;
    11. rank[i] = 1;
    12. }
    13. }
    14. // 获取size
    15. @Override
    16. public int getSize(){
    17. return parent.length;
    18. }
    19. // 查看索引对应的集合
    20. public int find(int index){
    21. if (index < 0 || index > id.length) {
    22. throw new IllegalArgumentException("out of index");
    23. }
    24. // 要找到 root ,返回 所属的 root 对应的结合ID
    25. // root 节点的值与自身索引相同
    26. while(index != parent[index]){
    27. index = parent[index];
    28. }
    29. return index;
    30. }
    31. // 查看两个元素可连接
    32. @Override
    33. public boolean isConnected(int p, int q){
    34. return find(p) == find(q);
    35. }
    36. @Override
    37. // 修改其中一个节点的ID
    38. // 之后需要将所有均改为同一ID
    39. public void unionElements(int p, int q){
    40. // 获取父节点
    41. int pParent = find(p);
    42. int qParent = find(q);
    43. // 如果两节点相同
    44. if(pParent == qParent){
    45. return;
    46. }else{
    47. if(rank[qParent] > rank[pParent]){
    48. parent[pParent] = qParent;
    49. }else if(rank[qParent] < rank[pParent]){
    50. parent[qParent] = pParent;
    51. }else{
    52. parent[pRoot] = qRoot;
    53. rank[qRoot] +=1;
    54. }
    55. }
    56. }
    57. }
  • 直接让 索引的上移节点指向 root 节点,减少高度 , 进行路径压缩

  • 让同一高度下,子节点数较多

image.png
image.png

// 在查找过程中进行的优化
public int find(int index){
        if (index < 0 || index > id.length) {
                throw new IllegalArgumentException("out of index");
            }
        // 要找到 root ,返回 所属的 root 对应的结合ID
        // root 节点的值与自身索引相同
        while(index != parent[index]){
            parent[index] = parent[parent[index]];
            index = parent[index];
        }
        return index;
    }