B站左程云算法视频基础提升02

岛问题

一个矩阵中只有0和1两种值,每个位置都可以和自己的上下左右相连,如果一片1连在一起,这个部分叫做一个岛,求一个矩阵有多少个岛

思路:遍历,infect

class Solution {
    public int numIslands(char[][] grid) {
        int res = 0;
        if(grid == null || grid[0] == null ){
            return 0;
        }
        int N = grid.length;
        int M = grid[0].length;
        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 || i >= N || j < 0 || j >=M || grid[i][j] != '1'){
            return;
        }
        grid[i][j] = '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);

    }
}

整体遍历阶段调用1次,infect4次,时间复杂度O(N*M)

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

特别大的二维数组分片计算岛,然后再union

并查集     Boolean isSameSet(a,b)      void union(a,b)

链表表示时,isSameSet时不快,不是O(1),哈希表时,union不是O(1)

并查集:往上指的图结构

每个集合都有一个代表元素

union,少的顶部挂在数量多的顶部

 

并查集的优化:往上找时,isSame(Y,?),找到顶点时,扁平化,整条连都各自直接指向顶节点。

//样本进来会包一层,叫做元素
public static class Element<V> {
    public V value;
    
    public Element(V value){
        this.value = value;
    }
}

public static class UnionFindSet<V> {
    public HashMap<V, Element<V>> elementMap;
    //key 某个元素 value 该元素的父
    public HashMap<Element<V>, Element<V>> fatherMap;
    //key 某个集合的代表元素 value该集合的大小
    public HashMap<Element<V>, Integer> sizeMap;

    public UninFindSet(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);
            sizeMap.put(element, 1);
        }
    }
    //给定一个元素,一直网上找,把代表元素返回
    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 boolean isSameSet(V a, V b){
        if(elementMap.containsKey(a) && elementMap.containsKey(b)){
            return findHead(elementMap.get(a)) == findHead(elementMap.get(b));
        }
        return false;
    }

    public void union(V a, V b){
        if (elementMap.containsKey(a) && elementMap.containsKey(b)){
            Element<V> aF = findHead(elementMap.get(a));
            Element<V> bF = findHead(elementMap.get(b));
            if(aF != bF){
                Element<V> big = sizeMap.get(aF) >= sizeMap.get(bF) ? aF : bF;
                Element<V> small = big == aF ? bF : aF;
                fatherMap.put(samll, big);
                sizeMap.put(big, sizeMpa.get(aF) + sizeMap.get(bF));
                sizeMap.remove(small);
            }
        }
    }
}

 扁平化使得并查集速度加快

结论:N个样本,findHead的调用次数达到o(N)的复杂度后,单次findHead的复杂度o(1),

如何并行计算

当有两个CPU时,分一半计算

{A},{B},{C},{D},四个集合相碰合并,合并后只有一个岛

合并逻辑:每一侧在感染后,收集边界信息,边界相碰讨论

当有多个CPU时,讨论四个边界信息

 

KMP算法解决的问题

字符串str1和str2,str1是否包含str2,如果包含返回str2在str1中开始的位置,如何做到时间复杂度O(N)?

str1"ABC1234de"       str2"1234"

暴力解法:尝试每一个开头

“111111111111112”     “1112”                       时复杂度较高o(N*M)

KMP算法:讨论哪一个开头能配出str2,无则返回-1

最长前缀和后缀的匹配长度

 

 对str2求该信息

KMP过程

 

1)j开头判断是否配出str2

2)i和j之间的位置配不出和str2相同的串(反证法,与更长的前缀后缀匹配矛盾)

str2不能再往前跳的时候,i++

public static int KMP(String s, String m){
    if (s == null || m == null || m.length() < 1 || s.length() < m.length()){
        return -1;
    }
    char[] str1 = s.toCharArray();
    char[] str2 = m.toCharArray();
    int i1 = 0;
    int i2 = 0;
    int[] mext = getNextArray(str2);//o(M)
    //O(N)
    while( i1 < str1.length && i2 < str.length ){
        if (str1[i1] == str2[i2]){
            i1++;
            i2++;
        }else if(next[i2] == -1){//i2 = 0 str2中的位置已经无法往前跳了
            i1++;
        }else{
            i2 = next[i2];
        }
    }
    //i1越界或者i2越界
    return i2 == str2.length ? i1 - i2 : -1;
}

public static int[] getNextArray(char[] ms){
    if(ms.length == 1){
        return new int[]{ -1 };
    }
    int[] next = new int[ms.length];
    next[0] = -1;
    next[1] = 0;
    int i = 2;//next的数组
    int cn = 0;
    while(i < next.length ){
        if (ms[i - 1] == ms[cn]){
            next[i++] = ++cn;
        }
        //当前跳到cn位置的字符,和i-1位置的字符配不上
        else if(cn > 0){
            cn = next[cn];
        }else{
            next[i++] = 0;
        }
    }
    return next;
}

复杂度也是O(2M)

N>M

KMP的复杂度为o(N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值