好久没写文章的我来水一篇了。
今天带来的是Leetcode200 的一题,难度中等。
先上题目。
给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
题意简单明了,思路也很容易有,最起码的可以dfs搜索配合染色,,更有技术含量的可以是并查集,其实我觉得这里得染色和并查集倒是一种原理,但是并查集显然会很妙。
并查集还是一种很有意思得算法,粗略记录一下自己得心得。
两个不同的点,因为某种原因联系了起来,于是用一条连来连接。越来越多得点,有的有联系,有的无联系,这时候就像是一个个派别,互相有关联得点联合在一起,变成了一棵树,
于是产生了几个派别,如果这时候发现一个派别下得一个点,和另一个派别可以有联系,那就可以强强联合,将此派别并入另一个派别下,成为了一体。
我们需要的是找每一个派别的老大,即祖结点。我们可以通过压缩路径来缩减时间复杂度。因为我们不必把每一个新找到的点并在子节点以下,这样互相查找会花更多时间,我们找到祖结点然后直接把它并入祖结点以下,这样到了最后,我们的一棵树只有两层了,祖结点和叶节点。。
如这题,两个1之间如果有联系,直接并成一棵树,最后看有几棵树就是答案了。
那么怎么并呢,我们需要查找到每一个节点的祖结点,如果他们的祖结点是一样,就不用并了,已经是一棵树。如果不一样,那么将其中一个祖结点变成另一个的子节点,注意处理后要压缩路径。
我没有用并查集写,但是效率也不算低了。
讲下我的思路,找到一个1直接res + 1,而后从这个1开始,将所有与它有关的1变为0,因为他们不可以算在res里面。非常简单,很容易看懂!
以下是在leetcode通过的Java代码。
class Solution {
public int numIslands(char[][] grid) {
int res = 0;
for(int i = 0; i < grid.length; i ++) {
for(int j = 0; j < grid[0].length; j ++) {
if(grid[i][j] == '1') {
res += 1;
dfs(i, j, grid);
}
}
}
return res;
}
public void dfs(int x, int y, char[][] grid) {
grid[x][y] = '0';
if(x > 0 && grid[x - 1][y] == '1') {
dfs(x - 1, y, grid);
}
if(y > 0 && grid[x][y - 1] == '1') {
dfs(x, y - 1, grid);
}
if(x < grid.length - 1 && grid[x + 1][y] == '1') {
dfs(x + 1, y, grid);
}
if(y < grid[0].length - 1 && grid[x][y + 1] == '1') {
dfs(x, y + 1, grid);
}
}
}