1. 有了130题的思路就好写多了,放上自己写的代码.
2. 没有用visited记录使用过的值,而是改用不同的符号代替,这是借鉴130题的思路.
3. 搜索的时候四个方向,注意学习.
4. 此题不需要回溯,因为要把所有的1都标记为岛屿,不像79题单词回溯时搜索单词不对要回溯.
class Solution {
public:
void dfs(int i, int j, vector<vector<char>>& grid) {
if (i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size())
return;
if (grid[i][j] == '#')
return;
if (grid[i][j] == '1') {
grid[i][j] = '#';
dfs(i + 1, j, grid);
dfs(i - 1, j, grid);
dfs(i, j + 1, grid);
dfs(i, j - 1, grid);
}
}
int numIslands(vector<vector<char>>& grid) {
if (grid.size() == 0 || grid[0].size() == 0)
return 0;
int m = grid.size(), n = grid[0].size();
int count = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
dfs(i, j, grid);
count++;
}
}
}
return count;
}
};
二. 参考一下大神的理解.
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/
1. 这道题是可以使用一个经典的算法来解决的,那就是 Flood fill,以下的定义来自 维基百科:Flood fill 词条。
Flood fill 算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典 算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名。在 GNU Go 和 扫雷 中,Flood Fill算法被用来计算需要被清除的区域。
2. 从一个区域中提取若干个连通的点与其他相邻区域区分开.从一个点扩散开,找到与其连通的点,这不是什么高深的算法,其实就是从一个点开始,进行一次 “深度优先遍历” 或者 “广度优先遍历”,通过 “深度优先遍历” 或者 “广度优先遍历” 发现一片连着的区域,对于这道题来说,就是从一个是 “陆地” 的格子开始进行一次 “深度优先遍历” 或者 “广度优先遍历”,把与之相连的所有的格子都标记上,视为发现了一个 “岛屿”。
3. 深度优先遍历在之前写过了, 关键是广度优先遍历. 此时你就不用回溯了.“广度优先遍历” 需要一个 “辅助队列”.(动态图不错)(java代码直接看注释也很好理解.)
/**
* 方法二:广度优先遍历
*/
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,y
int[][] 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) 的点开始进行广度优先遍历
if (!marked[i][j] && grid[i][j] == '1') {
count++;
LinkedList<Integer> queue = new LinkedList<>();
// 小技巧:把坐标转换为一个数字
// 否则,得用一个数组存,在 Python 中,可以使用 tuple 存
queue.addLast(i * cols + j);
// 注意:这里要标记上已经访问过
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];
// 如果不越界、没有被访问过、并且还要是陆地,我就继续放入队列,放入队列的同时,要记得标记已经访问过
if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) {
queue.addLast(newX * cols + newY);
// 【特别注意】在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,你迟早都会遍历到它
// 而不是在出队列的时候再标记
// 【特别注意】如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的
marked[newX][newY] = true;
}
}
}
}
}
}
return count;
}