【最小生成树】(一) 预备知识 并查集

并查集

本质就是一个数组 int[] father;

father[i] = j 表示元素 i 属于 集合 j ;

" 集合 j " 说明这个集合以元素 j 为根; 只要一个元素 i 沿着 father 数组能往上找到 j , 那么 i 就属于根为 j 的集合;

例如 father = {1, 3, 3, 5, 1, 5};

我们希望找到 下标0 所属集合的根, father[0] = 1, father[1] = 3, father[3] = 5, father[5] = 5;
于是我们沿着 father 数组找到了 下标0 所属集合的根, 即 下标5;
并且这个集合中, 包含的元素有下标 {0, 1, 3, 5}

能做什么?

  1. 能快速求两个点是否在同一集合; 本文有例题;
  2. 能快速求集合大小; 本文有例题;
  3. 能快速判断无向图是否有环; 下一篇文章讲 Kruskal 算法时用到;

有哪些操作?

  1. 初始化一个并查集
class Union{
    private int[] father;
    
    public Union(int size){
        father = new int[size];
        // father[i] = i 表示 i 是当前集合的 根;
        for(int i = 0; i < size; i++){
            father[i] = i;
        }
    }
}
  1. 找到元素所属的集合(或者说找到元素的根)
 public int root(int i){
        int temp = i;
        while(father[temp] != temp){
            temp = father[temp];
        }
        // 路径压缩, 后面会介绍
        father[i] = temp;
        return temp;
    }
  1. 判断两个元素是否属于相同的集合(有相同的根)
 public boolean isSame(int i1, int i2){
 	return root(i1) == root(i2);
 }
  1. 合并两个元素所在的集合;
public void join(int i1, int i2){
	father[root(i2)] = root(i1);
}

性能如何?

一般情况下, 各个元素通过 father 数组在逻辑上构成一个 k叉树 的结构, 树的形状与插入以及合并的具体操作息息相关, 最坏时间复杂度为 O(n);

好消息是, 我们可以进行[路径压缩]来进行性能优化;

public int root(int i){
        int temp = i;
        while(father[temp] != temp){
            temp = father[temp];
        }
        // 路径压缩
        father[i] = temp;
        return temp;
}

通过此优化, 每次调用 root 函数 (isSame 和 join 都会调用 root), 都会使得元素 i 能直接一步找到集合的根;

如果进行 [路径压缩], 那么方法调用次数越多, 性能越好, 越接近于 O(1);

题目: 判断任意两点间是否存在路径

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();

        Union union = new Union(n);
        for(int i = 0; i < k; i++){
            union.join(sc.nextInt(), sc.nextInt());
        }
        
        int from = sc.nextInt();
        int to = sc.nextInt();
        System.out.println(union.isSame(from, to)? 1 : 0);
    }
}

class Union{
    private int[] father;
    
    public Union(int size){
        father = new int[size + 1];
        for(int i = 1; i <= size; i++){
            father[i] = i;
        }
    }
    
    public boolean isSame(int i1, int i2){
        return root(i1) == root(i2);
    }
    
    public void join(int i1, int i2){
        int root1 = root(i1);
        int root2 = root(i2);
        if(root1 != root2){
            father[root2] = root1;
        }
    }
    
    public int root(int i){
        int temp = i;
        while(father[temp] != temp){
            temp = father[temp];
        }
        father[i] = temp;
        return temp;
    }
}

题目: 求岛屿的最大面积

在这里插入图片描述
res = 4

import java.util.*;

public class Main{
    public static void main(String[] args){
 		Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        boolean[][] grid = new boolean[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                grid[i][j] = (sc.nextInt() == 1);
            }
        }
        System.out.println(getMaxAreaUnion(grid));
    }
    
	private static int getMaxAreaUnion(boolean[][] grid){
        int m = grid.length;
        int n = grid[0].length;
        // 对坐标 i, j 进行编码, 好与并查集适配;
        // 点[i,j] 在并查集中的下标为 i * n + j
        Union union = new Union(m * n);
    
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j]){
                   union.add(i * n + j);
                   if(i > 0 && grid[i - 1][j]){
                       union.join(i * n + j, (i - 1) * n + j);
                   }
                   if(j > 0 && grid[i][j - 1]){
                       union.join(i * n + j, i * n + j -1);
                   }
                }
            }
        }
            
        return union.getMaxArea();
    }
}

class Union{
    private int[] father;
    // 并查集变种, 增加统计集合大小的功能
    private int[] area;
    private int maxArea;
    
    public Union(int size){
        father = new int[size];
        for(int i = 0; i < size; i++){
            father[i] = i;
        }
        area = new int[size];
    }
    
    public int root(int index){
        int root = index;
        while(root != father[root]){
            root = father[root];
        }
        father[index] = root;
        return root;
    }
    // 合并岛屿时, 需要将root2的面积, 加到root1
    public void join(int i1, int i2){
        int root1 = root(i1);
        int root2 = root(i2);
        if(root1 != root2){
            area[root1] += area[root2];
            father[root2] = root1;
            maxArea = Math.max(maxArea, area[root1]);
        }
    } 
    
    // 遇到一个值为 1 的点, 就将其标记为岛屿, 面积设置为1
    public void add(int index){
        area[index] = 1;
        if(maxArea == 0){
            maxArea = 1;
        }
    }
    
    public int getMaxArea(){
        return maxArea;
    }
}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值