16.3: Número de islas Pregunta 2

número de islas pregunta 2

https://leetcode.cn/problems/number-of-islands-ii/

Se le da una cuadrícula binaria m x nde grid. Una cuadrícula representa un mapa donde , 0representa agua y 1representa tierra. Inicialmente, gridtodas las celdas son celdas de agua (es decir, todas las celdas son 0).

El agua en un lugar se puede convertir en tierra realizando addLandla operación . Se le da una matriz positionsdonde positions[i] = [ri, ci]está ila posición en la que realizar la operación -ésima (ri, ci).

Devuelve una matriz de enteros answerdonde answer[i]es (ri, ci)el número de islas en el mapa después de convertir la celda en tierra.

La definición de una isla es una "tierra" rodeada de "agua", conectada por tierra adyacente en dirección horizontal o dirección vertical. Puede suponer que los cuatro lados de la cuadrícula del mapa están rodeados de "agua" sin fin.

Ejemplo 1:

imagen
输入:m = 3, n = 3, positions = [[0,0],[0,1],[1,2],[2,1]]
输出:[1,1,2,3]
解释:
起初,二维网格 grid 被全部注入「水」。(0 代表「水」,1 代表「陆地」)
- 操作 #1:addLand(0, 0) 将 grid[0][0] 的水变为陆地。此时存在 1 个岛屿。
- 操作 #2:addLand(0, 1) 将 grid[0][1] 的水变为陆地。此时存在 1 个岛屿。
- 操作 #3:addLand(1, 2) 将 grid[1][2] 的水变为陆地。此时存在 2 个岛屿。
- 操作 #4:addLand(2, 1) 将 grid[2][1] 的水变为陆地。此时存在 3 个岛屿。

Ejemplo 2:

输入:m = 1, n = 1, positions = [[0,0]]
输出:[1]

pista:

  • 1 <= m, n, positions.length <= 104
  • 1 <= m * n <= 104
  • positions[i].length == 2
  • 0 <= ri < m
  • 0 <= ci < n

entender el tema

De acuerdo con la Lista pública estática numIslands21(int m, int n, int[] [] posiciones) {} , donde m se refiere a las filas de la matriz bidimensional, n se refiere a las columnas de la matriz bidimensional y la Las posiciones de matriz bidimensional se refieren a las coordenadas de nodo del 1 entrante.

Por ejemplo: m = 2, n=3, posiciones = { {0,0},{0,2},{1,1},{1,2}}, que se refiere a dos filas y tres columnas El {0 Las posiciones ,0}, {0,2}, {1,1}, {1,2} en la matriz dimensional son todas 1 y las demás posiciones son todas 0. Tenga en cuenta que el orden de las coordenadas aquí está codificado y el orden de aparición también está codificado.

El punto más importante es: cuando se desplaza a la posición {0,2}, las posiciones {1,1}, {1,2} que no son convenientes para ir más tarde se considerarán como 0, y {0,0 } posición atravesada Es 1. Así que simplemente puedes imaginar que aparecerá un 1 dondequiera que atravieses.

tren de pensamiento

1: planeo usar el método de búsqueda de unión, pero los 1 aquí aparecen al azar, por lo que no podemos inicializar la matriz padre y la matriz de tamaño tan pronto como aparezcamos (la forma de inicializar es la siguiente)

	public UnionFind2(char[][] board) {
    
    
            //行数
            int H = board.length;
            //列数
            int L = board[0].length;
            this.L = L;
            fatehr = new int[H * L];
            size = new int[H * L];
            help = new int[H * L];
            sets = 0;
            //遍历一遍board二维数组。
            for (int i = 0; i < H; i++) {
    
    
                for (int j = 0; j < L; j++) {
    
    
                    //只有二维数组board是’1‘的,一维数组才会放入数。
                    //这样一维数组就会有很多位置是空的,但是这不重要。
                    if (board[i][j] == '1') {
    
    
                        //将二维数组下标转化为一维数组的下标。
                        int k = index(i, j);
                        fatehr[k] = k;
                        size[k] = 1;
                        sets++;
                    }
                }
            }
        }

Necesitamos una inicialización dinámica, es decir, inicializar el padre y el tamaño de quien nos encontremos.

2: Sé que al fusionar, si se pueden combinar dos números, entonces debe ir acompañado de conjuntos–, y tamaño[pequeño] = 0. La cantidad de colecciones definitivamente disminuirá y la cantidad de colecciones de pequeños ancestros se borrará. Pero aquí usamos size[k] != 0 para indicar que el nodo es una isla, y size[k] == 0 para indicar que el nodo es agua. Por lo tanto, cuando se fusiona la colección, no se puede borrar directamente, solo manténgala sin cambios. Es conveniente que los nuevos nodos juzguen que el nodo actual es una isla.

package algorithmbasic.class16;
// https://leetcode.cn/problems/number-of-islands-ii/

import java.util.ArrayList;
import java.util.List;

public class numIslands2 {
    
    

    public static List<Integer> numIslands21(int m, int n, int[][] positions) {
    
    
        //对并查集进行初始化操作,创建好一维数组father,size,以及辅助数组help。设置记录集合数量的变量sets。
        UnionFind1 unionFind1 = new UnionFind1(m, n);
        List<Integer> ans = new ArrayList<>();
        for (int[] p : positions) {
    
    // -------------------------- 二维数组遍历的新方法。
            ans.add(unionFind1.connect(p[0], p[1]));
        }
        return ans;
    }

    /**
     * 并查集内部类。
     */
    public static class UnionFind1 {
    
    
        /**
         * 属性
         */
        static int[] father;
        static int[] size;
        static int[] help;
        static int sets;
        //列的长度大小
        static int L;
        //行的长度大小
        static int H;

        /**
         * 构造器
         * 这里我们没有一上来就直接进行初始化操作,我们需要走一步看一步。
         */
        public UnionFind1(int m, int n) {
    
    
            father = new int[m * n];
            size = new int[m * n];
            help = new int[m * n];
            sets = 0;
            L = n;
            H = m;
        }

        /**
         * index方法
         * 传入二维数组的行列参数:i,j 找到对应的一维数组的下标 k
         * 公式:K = i * positions[0].length + j
         */

        public static int index(int i, int j) {
    
    
            return i * L + j;
        }

        /**
         * findAncestor方法
         * 传入一个节点,然后从这个节点开始一直往上找,直到直到最上边为止,返回最上面的节点。
         */
        public static int findAncestor(int cur) {
    
    
            int j = 0;
            while (cur != father[cur]) {
    
    
                //进行优化,将途径的节点进行记录。
                help[j++] = cur;
                cur = father[cur];
            }
            //cur == father[cur]
            j--;
            while (j >= 0) {
    
    
                father[help[j--]] = cur;
            }
            return cur;
        }


        /**
         * union合并方法
         * 将 i,j   m,n两个位置所在的集合进行合并
         */

        public static void union(int i, int j, int m, int n) {
    
    
            //防止越界条件
            if (m < 0 || m >= H || n < 0 || n >= L) {
    
    
                return;
            }
            //根据二维数组的下标找到一维数组的下标。
            int i1 = index(i, j);
            int i2 = index(m, n);
            //只有都是岛屿时才可以合并。
            if (size[i1] != 0 && size[i2] != 0) {
    
    
                //找到各自节点的祖先节点。
                int fatherA = findAncestor(i1);
                int fatherB = findAncestor(i2);
                //只有祖先不同时才可以合并,防止祖先相同时反复合并,反复的sets--,导致数据不准确。
                if (fatherA != fatherB) {
    
    
                    //找到祖先节点fatherA所在集合大小sizeA。
                    int sizeA = size[fatherA];
                    //找到祖先节点fatherB所在集合大小sizeB。
                    int sizeB = size[fatherB];
                    //big指向集合数量较多的祖先节点。
                    int big = sizeA > sizeB ? fatherA : fatherB;
                    //small指向集合数量较少的祖先节点。
                    int small = big == fatherA ? fatherB : fatherA;
                    //进行合并
                    //注意这个地方size[small]不要被置为0.
                    father[small] = big;
                    size[big] = size[big] + size[small];
                    sets--;
                }
            }
        }


        /**
         * connect连接方法
         * 传入两个参数 i,j 说明二维数组positions的 i,j位置出现了1,然后对其进行相连,连接完之后,返回二维数组目前一共有多少集合。
         */
        public static int connect(int i, int j) {
    
    
            int k = index(i, j);
            //之前
            if (size[k] == 0) {
    
    
                //初始化当前位置的father与size数组。
                size[k] = 1;
                father[k] = k;
                sets++;
                //进行上下左右的合并。
                union(i, j, i + 1, j);
                union(i, j, i - 1, j);
                union(i, j, i, j + 1);
                union(i, j, i, j - 1);
            }
            return sets;
        }
    }
}

complejidad del tiempo

La complejidad del tiempo es O (m * n + k), k se refiere al número de 1 en la matriz de posición es k.

m*n es la complejidad de la búsqueda de unión de inicialización, porque al construir el objeto de búsqueda de unión, asignará espacio para la estructura de datos del objeto de búsqueda de unión y realizará la operación de inicialización.

padre = nuevo int[m * n]; tamaño = nuevo int[m * n]; ayuda = nuevo int[m * n]; la complejidad del tiempo es m*n.

Método dos:

En el primer método, si m*n es relativamente grande, se someterá a una fuerte inicialización y k es relativamente pequeño, por lo que se adopta el siguiente método y su complejidad temporal es O(k)

Solo es necesario inicializar el nodo que es 1, y no es necesario inicializar otros nodos.

package algorithmbasic.class16;
// https://leetcode.cn/problems/number-of-islands-ii/

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 方法二:时间复杂度是O(K)
 * 课上讲的如果m*n比较大,会经历很重的初始化,而k比较小,怎么优化的方法
 */

public class numIslands2_2 {
    
    

    public static List<Integer> numIslands22(int m, int n, int[][] positions) {
    
    
        List<Integer> ans = new ArrayList<>();
        UnionFind2 unionFind2 = new UnionFind2(m, n);
        for (int[] p : positions) {
    
    
            ans.add(unionFind2.connect(p[0], p[1]));
        }
        return ans;
    }


    /**
     * 并查集内部类
     */
    public static class UnionFind2 {
    
    
        /**
         * 属性
         */
        //之前写的并查集我们需要额外建立一个Dot类来区分相同的元素
        //其实可以直接用一个String来区分,比如说3行4列 写成 ->"3_4"
        static HashMap<String, String> fatherMap;
        static HashMap<String, Integer> sizeMap;
        static int sets;
        //行
        static int H;
        //列
        static int L;
        //辅助数组
        static ArrayList<String> list;

        /**
         * 构造器
         */
        public UnionFind2(int m, int n) {
    
    
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            list = new ArrayList<>();
            sets = 0;
            this.H = m;
            this.L = n;
        }

        /**
         * findAncestor方法
         * 创建一个参数,直至找到他的祖先
         */
        public static String findAncestor(String k) {
    
    

            while (!fatherMap.get(k).equals(k)) {
    
    
                list.add(k);
                k = fatherMap.get(k);
            }
            // k == fatherMap.get(k).
            for (String s : list) {
    
    
                fatherMap.put(s, k);
            }
            list.clear();
            return k;
        }

        /**
         * union方法
         */
        public static void union(String key, String k) {
    
    
            //如果有传进来的k,就进行合并。
            if (fatherMap.containsKey(k)) {
    
    
                //找到key的祖先fatherA
                String fatherA = findAncestor(key);
                //找到k的祖先fatherB
                String fatherB = findAncestor(k);
                //如果两个祖先不一样的就合并。
                if (!fatherA.equals(fatherB)) {
    
    
                    //big指向集合比较大的祖先
                    String big = sizeMap.get(fatherA) > sizeMap.get(fatherB) ? fatherA : fatherB;
                    String small = big == fatherA ? fatherB : fatherA;
                    //进行合并
                    fatherMap.put(small, big);
                    sizeMap.put(big, sizeMap.get(big) + sizeMap.get(small));
                    //sizeMap.remove(small);
                    //这个地方还不能remove,因为有可能其他节点会连接他。
                    sets--;
                }
            }
        }

        /**
         * connect方法
         * 传入两个参数 i,j 说明二维数组positions的 i,j位置出现了1,然后对其进行相连,连接完之后,返回二维数组目前一共有多少集合。
         */

        public static int connect(int i, int j) {
    
    
            //先判断一下这个坐标之前出现过没,如果之前出现过,说明早已连接好,就跳过
            String key = String.valueOf(i) + "_" + String.valueOf(j);
            if (!fatherMap.containsKey(key)) {
    
    
                //进行动态的初始化。
                fatherMap.put(key, key);
                sizeMap.put(key, 1);
                sets++;
                //上并进行边界判断
                String up = (i - 1 < H && i >= 0) ? String.valueOf(i - 1) + "_" + String.valueOf(j) : null;
                if (up != null) union(key, up);
                //下
                String down = (i + 1 < H && i >= 0) ? String.valueOf(i + 1) + "_" + String.valueOf(j) : null;
                if (down != null) union(key, down);
                //左
                String left = (j - 1 < L && j >= 0) ? String.valueOf(i) + "_" + String.valueOf(j - 1) : null;
                if (left != null) union(key, left);
                //右
                String right = (j + 1 < L && j >= 0) ? String.valueOf(i) + "_" + String.valueOf(j + 1) : null;
                if (right != null) union(key, left);

                union(key, up);
                union(key, down);
                union(key, left);
                union(key, right);
            }
            return sets;
        }
    }

    public static void main(String[] args) {
    
    
        int[][] arr = {
    
    {
    
    0, 1}, {
    
    1, 2}, {
    
    2, 1}, {
    
    1, 0}, {
    
    0, 2}, {
    
    0, 0}, {
    
    1, 1}};
        List<Integer> list = numIslands2_2.numIslands22(3, 3, arr);
        System.out.println(list);
    }
}

Supongo que te gusta

Origin blog.csdn.net/SCR1209/article/details/131024173
Recomendado
Clasificación