日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[Leedcode][JAVA][第200题][岛屿数量][DFS][BFS][并查集]

發(fā)布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Leedcode][JAVA][第200题][岛屿数量][DFS][BFS][并查集] 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

【問題描述】 第200題 島嶼數(shù)量

給你一個由?'1'(陸地)和 '0'(水)組成的的二維網(wǎng)格,請你計算網(wǎng)格中島嶼的數(shù)量。島嶼總是被水包圍,并且每座島嶼只能由水平方向和/或豎直方向上相鄰的陸地連接形成。此外,你可以假設該網(wǎng)格的四條邊均被水包圍。示例 1:輸入: 11110 11010 11000 00000 輸出:?1 示例?2:輸入: 11000 11000 00100 00011 輸出: 3 解釋: 每座島嶼只能由水平和/或豎直方向上相鄰的陸地連接而成。

【解答思路】

1. 深度優(yōu)先遍歷

時間復雜度:O(N^2) 空間復雜度:O(N)

/*** 方法一:深度優(yōu)先遍歷*/ public class Solution {// x-1,y// x,y-1 x,y x,y+1// x+1,y// 方向數(shù)組,它表示了相對于當前位置的 4 個方向的橫、縱坐標的偏移量,這是一個常見的技巧private static final int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};// 標記數(shù)組,標記了 grid 的坐標對應的格子是否被訪問過private boolean[][] marked;// grid 的行數(shù)private int rows;// grid 的列數(shù)private int cols;private char[][] grid;public int numIslands(char[][] grid) {rows = grid.length;if (rows == 0) {return 0;}cols = grid[0].length;this.grid = grid;marked = new boolean[rows][cols];int count = 0;for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {// 如果是島嶼中的一個點,并且沒有被訪問過// 就進行深度優(yōu)先遍歷if (!marked[i][j] && grid[i][j] == '1') {count++;dfs(i, j);}}}return count;}// 從坐標為 (i,j) 的點開始進行深度優(yōu)先遍歷private void dfs(int i, int j) {marked[i][j] = true;// 得到 4 個方向的坐標for (int k = 0; k < 4; k++) {int newX = i + directions[k][0];int newY = j + directions[k][1];// 如果不越界、沒有被訪問過、并且還要是陸地if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) {dfs(newX, newY);}}}// 封裝成 inArea 方法語義更清晰private boolean inArea(int x, int y) {// 等于號不要忘了return x >= 0 && x < rows && y >= 0 && y < cols;}public static void main(String[] args) {Solution solution = new Solution();char[][] grid1 = {{'1', '1', '1', '1', '0'},{'1', '1', '0', '1', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '0', '0', '0'}};int numIslands1 = solution.numIslands(grid1);System.out.println(numIslands1);char[][] grid2 = {{'1', '1', '0', '0', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '1', '0', '0'},{'0', '0', '0', '1', '1'}};int numIslands2 = solution.numIslands(grid2);System.out.println(numIslands2);} }
2. 廣度優(yōu)先遍歷

時間復雜度:O(N) 空間復雜度:O(N)

import java.util.LinkedList;/*** 方法二:廣度優(yōu)先遍歷*/ public class Solution2 {private int rows;private int cols;public int numIslands(char[][] grid) {// x-1,y// x,y-1 x,y x,y+1// x+1,yint[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};rows = grid.length;if (rows == 0) {return 0;}cols = grid[0].length;boolean[][] marked = new boolean[rows][cols];int count = 0;for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {// 如果是島嶼中的一個點,并且沒有被訪問過// 從坐標為 (i,j) 的點開始進行廣度優(yōu)先遍歷if (!marked[i][j] && grid[i][j] == '1') {count++;LinkedList<Integer> queue = new LinkedList<>();// 小技巧:把坐標轉換為一個數(shù)字// 否則,得用一個數(shù)組存,在 Python 中,可以使用 tuple 存queue.addLast(i * cols + j);// 注意:這里要標記上已經(jīng)訪問過marked[i][j] = true;while (!queue.isEmpty()) {int cur = queue.removeFirst();int curX = cur / cols;int curY = cur % cols;// 得到 4 個方向的坐標for (int k = 0; k < 4; k++) {int newX = curX + directions[k][0];int newY = curY + directions[k][1];// 如果不越界、沒有被訪問過、并且還要是陸地,我就繼續(xù)放入隊列,放入隊列的同時,要記得標記已經(jīng)訪問過if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) {queue.addLast(newX * cols + newY);// 【特別注意】在放入隊列以后,要馬上標記成已經(jīng)訪問過,語義也是十分清楚的:反正只要進入了隊列,你遲早都會遍歷到它// 而不是在出隊列的時候再標記// 【特別注意】如果是出隊列的時候再標記,會造成很多重復的結點進入隊列,造成重復的操作,這句話如果你沒有寫對地方,代碼會嚴重超時的marked[newX][newY] = true;}}}}}}return count;}private boolean inArea(int x, int y) {// 等于號這些細節(jié)不要忘了return x >= 0 && x < rows && y >= 0 && y < cols;}public static void main(String[] args) {Solution2 solution2 = new Solution2();char[][] grid1 = {{'1', '1', '1', '1', '0'},{'1', '1', '0', '1', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '0', '0', '0'}};int numIslands1 = solution2.numIslands(grid1);System.out.println(numIslands1);char[][] grid2 = {{'1', '1', '0', '0', '0'},{'1', '1', '0', '0', '0'},{'0', '0', '1', '0', '0'},{'0', '0', '0', '1', '1'}};int numIslands2 = solution2.numIslands(grid2);System.out.println(numIslands2);} }
3. 并查集

public class Solution {

public int numIslands(char[][] grid) {int rows = grid.length;if (rows == 0) {return 0;}int cols = grid[0].length;int size = rows * cols;// 兩個方向的方向向量(理解為向下和向右的坐標偏移)int[][] directions = {{1, 0}, {0, 1}};// +1 是認為虛擬的水域UnionFind unionFind = new UnionFind(size + 1);for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (grid[i][j] == '1') {for (int[] direction : directions) {int newX = i + direction[0];int newY = j + direction[1];if (newX < rows && newY < cols && grid[newX][newY] == '1') {unionFind.union(cols * i + j, cols * newX + newY);}}} else {// 如果不是陸地,所有的水域和一個虛擬的水域連接unionFind.union(cols * i + j, size);}}}// 減去那個一開始多設置的虛擬的水域return unionFind.count - 1; }class UnionFind {private int[] parent;private int count;public UnionFind(int n) {this.count = n;parent = new int[n];for (int i = 0; i < n; i++) {parent[i] = i;}}/*** 返回索引為 p 的元素的根結點** @param p* @return*/public int find(int p) {// 在 find 的時候執(zhí)行路徑壓縮while (p != parent[p]) {// 兩步一跳完成路徑壓縮,這里是「隔代壓縮」// 說明:「隔代壓縮」和「按秩合并」選擇一個實現(xiàn)即可,「隔代壓縮」的代碼量少,所以選它parent[p] = parent[parent[p]];p = parent[p];}return p;}public boolean connected(int p, int q) {int pRoot = find(p);int qRoot = find(q);return pRoot == qRoot;}public void union(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;}parent[qRoot] = pRoot;// 每次 union 以后,連通分量減 1count--;} }

}

【總結】

1. 深度遍歷
  • 遞歸
2. 廣度遍歷
  • 隊列
  • 所有加入隊列的結點,都應該馬上被標記為 “已經(jīng)訪問”,否則有可能會被重復加入隊列
3.并查集學習資料

https://liweiwei1419.gitee.io/leetcode-algo/leetcode-by-tag/union-find/

參考鏈接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/

總結

以上是生活随笔為你收集整理的[Leedcode][JAVA][第200题][岛屿数量][DFS][BFS][并查集]的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。