数据结构与算法--并查集结构

数据结构与算法--并查集结构

 


 

1 岛问题

 

一个矩阵中只有0和1两种值,每个位置都可以和自己的上、下、左、右 四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求一个矩阵中有多少个岛?
【举例】
001010
111010
100100
000000
这个矩阵中有三个岛

coding

package com.chao;

/**
 * @author Mrchao
 * @version 1.0.0
 * @date 2023-10-15
 */
public class CountIslandTest {

    public static void main(String[] args) {
        int[][] m = {
                {0, 0, 1, 0, 1, 0},
                {1, 1, 1, 0, 1, 0},
                {1, 0, 0, 1, 0, 0},
                {0, 0, 0, 0, 0, 0}
        };
        System.out.println(countIsland(m));
    }

    public static int countIsland(int[][] m) {
        if (m == null || m[0] == null) {
            return 0;
        }

        int M = m.length;//行数
        int N = m[0].length;//列数

        int ret = 0;
        /**
         * 遍历一遍岛屿 碰到 1 则进行一次感染过程
         */
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                if (m[i][j] == 1) {
                    ret++;
                    infect(m, i, j, M, N);
                }
            }
        }
        return ret;
    }

    /**
     * 二维数组 m 中与 m[i][j]直接相邻 或间接相邻的点 全部修改为 2
     *
     * @param m
     * @param i
     * @param j
     * @param M 二维数组行数
     * @param N 二维数组的列数
     */
    public static void infect(int[][] m, int i, int j, int M, int N) {
        if (i < 0 || i >= M || j < 0 || j >= N || m[i][j] != 1) {
            return;
        }
        m[i][j] = 2;
        infect(m, i - 1, j, M, N);
        infect(m, i + 1, j, M, N);
        infect(m, i, j - 1, M, N);
        infect(m, i, j + 1, M, N);
    }
}

 

时间复杂度 O ( M ∗ N ) O(M * N) O(MN)

 


 

2 并查集结构

 

并查集对外提供的操作 :

  1. 集合的合并 合并两个集合 形成一个更大的集合

  2. 集合的查询 判断两个元素是否在同一个集合中

如图所示

 

public class UnionFindTest {
    // 并查集中的元素
    public static class Element<V> {
        public V value;

        public Element(V value) {
            this.value = value;
        }
    }

    public static class UnionFindSet<V> {
        public Map<V, Element<V>> elementMap;// key 某个具体的值 value key 对应的元素
        public Map<Element<V>, Element<V>> fatherMap;// key 并查集中的某个元素  value : 该元素的父 当前元素向上指的元素
        public Map<Element<V>, Integer> sizeMap;// key 代表元素  value 该集合中元素的个数

        public UnionFindSet(List<V> list) {
            elementMap = new HashMap<>();
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            for (V v : list) {
                Element<V> element = new Element<>(v);// 创建元素
                elementMap.put(v, element);
                fatherMap.put(element, element); // 当前每个节点的父都是自己
                sizeMap.put(element, 1); // 每个集合中的元素的个数都是 1
            }
        }

        /**
         * 给定一个元素一直往上找  把代表元素返回
         *
         * @param e
         * @return
         */
        private Element<V> findHead(Element<V> e) {
            Stack<Element<V>> path = new Stack<>();
            // 没有找到父 就一直找 
            while (e != fatherMap.get(e)) {
                path.push(e);
                e = fatherMap.get(e);
            }
            // 到此找到了 父元素
            // 把沿途遍历过的所有的元素都挂在当前的父元素上
            while (!path.isEmpty()) {
                fatherMap.remove(path.pop(), e);
            }
            return e;
        }

        /**
         * 判断 两个值是否在同一个集合中
         *
         * @param v1
         * @param v2
         * @return
         */
        public boolean isSameSet(V v1, V v2) {
            if (elementMap.containsKey(v1) && elementMap.containsKey(v2)) { // v1 v2 初始化过
                return findHead(elementMap.get(v1)) == findHead(elementMap.get(v2));
            }
            return false;
        }

        /**
         * 将两个值合并为一个集合
         *
         * @param v1
         * @param v2
         */
        public void union(V v1, V v2) {
            if (elementMap.containsKey(v1) && elementMap.containsKey(v2)) { // 被初始化过
                Element<V> fHead = findHead(elementMap.get(v1));
                Element<V> sHead = findHead(elementMap.get(v2));
                if (fHead != sHead) { // 不在同一个集合中 才进行合并
                    // 数据较少的集合的顶端挂在数据较多的数据的顶端的底下
                    Element<V> big = sizeMap.get(fHead) >= sizeMap.get(sHead) ? fHead : sHead;
                    Element<V> small = big == fHead ? sHead : fHead;
                    fatherMap.put(small, big);
                    sizeMap.put(big, sizeMap.get(fHead) + sizeMap.get(sHead));
                    sizeMap.remove(small);
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值