LeetCode 200. Number of Islands

LeetCode 200. Number of Islands

題目描述

題目連接java

思路1 感染函數

遍歷二維數組,設定一個全局變量s,表示島嶼數量,初始化爲0,遇到字符'1'就加將島嶼的數量+1(即s++),同時,把這個字符'1'及其周圍的全部字符'1'都「感染」成其餘的值,好比變爲'.' 這個字符,最後s的值即島嶼的數量。代碼和註釋見:c++

public static int numIslands(char[][] m) {
        if (null == m || 0 == m.length || m[0] == null || m[0].length == 0) {
            return 0;
        }
        int M = m.length;
        int N = m[0].length;
        int s = 0;
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                if (m[i][j] == '1') {
                    s++;
                    infect(m, i, j, M, N);
                }
            }
        }

        return s;
    }

    // note,這裏把M,N傳進去和不傳進去在leetcode中性能有差異,最好傳進去,後續就不須要繼續判斷了
    private static void infect(char[][] 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] = '.';
        infect(m, i + 1, j, M, N);
        infect(m, i, j + 1, M, N);
        infect(m, i - 1, j, M, N);
        infect(m, i, j - 1, M, N);
    }

思路2 並查集

暫時不用理解並查集這個結構的細節,咱們只須要知道,並查集能夠提供以下兩個方法,且複雜度均爲O(1)git

// 查詢樣本x和樣本y是否屬於一個集合
public boolean isSameSet(V x,V y)
// 把x和y各自所在集合的全部樣本合併成一個集合
public void union(V x,V y) 
// 返回一共的集合數量(在這題下含義就是島嶼的數量)
public int getSets()

咱們能夠先把第0行和第0列中'1'的字符拿到並創建集合,而後從第一行第一列開始遍歷剩餘位置,若是當前位置是'1'則看其周圍有沒有已經建好的'1'集合,有則union操做,無則單獨新建一個集合。代碼以下(把並查集看成黑盒):程序員

public static int numIslands(char[][] board) {

        int row = board.length;
        int col = board[0].length;
        UnionFind uf = new UnionFind(row, col, board);
        for (int j = 1; j < col; j++) {
            if (board[0][j - 1] == '1' && board[0][j] == '1') {
                uf.union(0, j - 1, 0, j);
            }
        }
        for (int i = 1; i < row; i++) {
            if (board[i - 1][0] == '1' && board[i][0] == '1') {
                uf.union(i - 1, 0, i, 0);
            }
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                if (board[i][j] == '1') {
                    if (board[i][j - 1] == '1') {
                        uf.union(i, j - 1, i, j);
                    }
                    if (board[i - 1][j] == '1') {
                        uf.union(i - 1, j, i, j);
                    }
                }
            }
        }
        return uf.getSets();
    }

接下來看具體的並查集的實現,並查集除了常規的扁平化優化操做之外,還能夠作一個小的優化,能夠將二維數組變成一維數組,這樣並查集中的size數組不須要設置爲二維數組,具體轉換方式是,即,任意MxN的二維數組,(i,j)位置,能夠轉換成一維數組的i * N + j + 1位置github

public static int oneArrIndex(int M, int N, int i, int j) {
   return i * N + j + 1;
}

完整並查集代碼見:面試

public static int oneArrIndex(int M, int N, int i, int j) {
        return i * N + j + 1;
    }

    public static class UnionFind {
        private int col;
        private int row;
        private int[] records;
        private int[] size;
        private int sets;

        public UnionFind(int row, int col, char[][] board) {
            this.row = row;
            this.col = col;
            int n = row * col + 1;
            // n的表明點就是records[n],由於二維數組的下標能夠轉換成一維數組下標(從1開始),因此能夠將二維數組某個點的表明點用records[n]表示
            // 其中n = oneArrIndex(i,j)
            records = new int[n];
            size = new int[n];

            for (int r = 0; r < row; r++) {
                for (int c = 0; c < col; c++) {
                    if (board[r][c] == '1') {
                        int i = oneArrIndex(row, col, r, c);
                        records[i] = i;
                        size[i] = 1;
                        sets++;
                    }
                }
            }
        }

        public int getSets() {
            return sets;
        }

        public void union(int x, int y, int x2, int y2) {
            int fa = find(oneArrIndex(row, col, x, y));
            int fb = find(oneArrIndex(row, col, x2, y2));
            if (fa != fb) {
                int sizeFb = size[fb];
                int sizeFa = size[fa];
                int all = sizeFa + sizeFb;
                if (sizeFa > sizeFb) {
                    records[fb] = fa;
                    size[fa] = all;
                    //size[fb] = all;
                } else {
                    records[fa] = fb;
                    // size[fa] = all;
                    size[fb] = all;
                }
                sets--;
            }
        }

        private int find(int a) {
            int t = a;
            while (t != records[t]) {
                t = records[t];
            }
            int ans = t;
            // 扁平化操做
            while (a != t) {
                int m = records[a];
                records[m] = t;
                a = m;
            }
            return ans;
        }
    }

更多

算法和數據結構筆記算法

參考資料

相關文章
相關標籤/搜索