算法与数据结构——并查集(java)(b站左程云课程笔记总结)

并查集

岛问题

image-20220620151922485

实现infected过程:

遍历,发现1的话就把该1及其相连的所有1都变成2,遍历过程中发现0和2就跳过

时间复杂度O(n*m)

大的循环每个调用一次

感染过程最多被调用四次

class Solution {
    public int numIslands(char[][] grid) {
        if(grid==null||grid.length==0||grid[0].length==0){
            return 0;
        }
        int n=grid.length;
        int m=grid[0].length;
        int res=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(grid[i][j]=='1'){
                    res++;
                    infect(grid,i,j,n,m);
                }
            }
        }
        return res;
    }

    public void infect(char[][] grid,int i,int j,int n,int m){
        if(i<0||j<0||i>=n||j>=m||grid[i][j]!='1'){//base case
            return;
        }
        grid[i][j]='2';//key point 不会死循环的原因:一上来就把当前位置改成2
        infect(grid,i+1,j,n,m);
        infect(grid,i-1,j,n,m);
        infect(grid,i,j+1,n,m);
        infect(grid,i,j-1,n,m);
    }
}

进阶:如何设计一个并行算法解决这个问题

可能会出现在大公司的面试场景中

一般的算法都是单线程

假设现在这个题目的二维数组特别大,需要用到并行算法,怎么解决

思路:先假设两个cpu,怎样比一个cpu快

把二维数组分成两块可能会导致岛的数量变多

把分开的边界上的 被感染的点标记,如果两个cpu处理的数据边界上能连起来,则使用并查集union,并使岛的总数量-1

现在的设计当然比一个cpu快,数据量大量减少+一个cpu整合一下边界即可

多个cpu同理:分块并分别收集四个边界信息,相邻的合并即可

image-20220620233554352

引入并查集

两个操作:isSameSet、union

传统的方式速度没法做到很快

  1. 链表的形式能快速做到union(O(1)),但isSameSet慢
  2. 哈希表isSameSet快(O(1)),但union慢

并查集要解决的问题:如何将两个操作都实现时间复杂度为O(1)

往上指的图结构

isSameSet:向上找直到不能再找,看最终结果是否一样

union:数量少的顶部连在数量多的顶部

image-20220620222151645

并查集的瓶颈在于向上找的过程链过长

向上找的过程可以优化

向上找的过程将链变成扁平化的,直接指向头节点

image-20220620222842331

//样本进来会包一层,叫做元素
//包一层的目的是一个样本可能有多个数据
public class Element<V>{
    public V value;
    public Element(V value){
        this.value=value;
    }
}

public class UnionFindSet<V>{
    public HashMap<V,Element<V>> elementMap;//a->a圈 b->b圈
    public HashMap<Element<V>,Element<V>> fatherMap;//value为key的父节点
    public HashMap<Element<V>,Integer> sizeMap;//key代表某个集合的代表元素,value表示该集合的大小
    
    
    //并查集初始化的时候需要样本
    public UnionFindSet(List<V> list){
        elementMap=new HashMap<>();
        fatherMap=new HashMap<>();
        sizeMap=new HashMap<>();
        for(V value:list){
            Element<V> element=new Element<V>(value);
            elementMap.put(value,element);
            fatherMap.put(element,element);//一开始fa
            sizeMap.put(element,1);
        }
    }
    
    
    public boolean isSameSet(V a,V b){
        if(elementMap.containsKey(a)&&elementMap.containsKey(b)){//保证a和b在并查集中注册过
            return findHead(elementMap.get(a))==findHead(elementMap.get(b));
        }
        return false;
    }
    
    //找头节点
    public Element<V> findHead(Element<V> element){
        Stack<Element<V> path=new Stack<>();//这里不一定要用栈,用一个容器装起来就可以了
        while(element!=fatherMap.get(element)){
            path.push(element);
            element=fatherMap.get(element);
        }
        while(!path.isEmpty()){//优化
            fatherMap.put(path.pop(),element);
        }
        return element;
    }
    
    public void union(V a,V b){
        if(elementMap.containsKey(a)&&elementMap.containsKey(b)){
            Element<V> aFather=findHead(elementMap.get(a));
            Element<V> bFather=findHead(elementMap.get(b));
            if(aFather!=bFather){
                Element<V> big=sizeMap.get(aFather)>=sizeMap.get(bFather)?aFather:bFather;
                Element<V> small=big==aFather?bFather:aFather;
                fatherMap.put(small,big);//数量较少的顶端挂在数量较多的顶端下面
                sizeMap.put(big,sizeMap.get(aFather)+sizeMap.get(bFather));//sizeMap update
                sizeMap.remove(small);//sizeMap  delete
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值