題目連接java
遍歷二維數組,設定一個全局變量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); }
暫時不用理解並查集這個結構的細節,咱們只須要知道,並查集能夠提供以下兩個方法,且複雜度均爲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; } }