有这样一道题:
是不是看起来很复杂,昨天听了左神讲的课,他用了一个简单的算法,时间复杂度只有O(N*M)数组长度那么少!
一开始看这个题,没有什么思路,想着这个题时间复杂都肯定很高吧!直到最后才发现,卧槽,还有这种操作!!!!
话不多说,直接上自己的代码!
package com.zuoshen;
public class Problem_02_Islands {
/* 给定一个二维数组,所有位置的值不是0就是1。规定每个位置可以和它上下左右位置上的值相连。
有一个叫做岛的概念,定义如下:
连成一片的1,如果周围都是0,那么这一片1,构成一个岛。
求整张图上有多少个岛。
例如:
0 0 0 0 0 0 0 0 0
0 1 1 0 0 1 1 1 0
0 1 1 1 0 0 0 1 0
0 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 0 0
0 0 0 0 1 1 1 0 0
0 0 0 0 0 0 0 0 0
这张图上有三个岛。
0 0 0 0 0 0 0 0 0
0 1 1 0 1 1 1 1 0
0 1 1 1 1 0 0 1 0
0 1 1 0 0 0 0 1 0
0 0 0 0 0 1 1 1 0
0 0 0 0 1 1 1 0 0
0 0 0 0 0 0 0 0 0
这张图上有一个岛。
进阶:
如果可以使用并行计算,如何来设计你的算法?*/
public static int countIslands(int[][] m) {
//判断2维数组合法
if (m == null || m[0] == null) {
return 0;
}
int N = m.length;
int M = m[0].length;
//res标记为1的元素,统计个数。
int res = 0;
//如果发现为1的元素,就去感染他周围(山下左右)的元素
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (m[i][j] == 1) {
res++;
infect(m, i, j, N, M);
}
}
}
return res;
}
//感染方法
public static void infect(int[][] m, int i, int j, int N, int M) {
//元素下标越界或!=1退出
if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) {
return;
}
m[i][j] = 2;
/*小技巧:找到1之后标记为2,(随便标记一个数就行)表示这个位置元素不再会被感染。
*
为什么呢?数组在遍历时碰到1,就会将1变为2,感染周围为1的元素,周围元素继续由1变2感染各自的周围为1的元素,依此递归,
直到一个岛的元素都被感染,被0包围,无法继续感染,退出递归,res++表示找到了一个岛。
这时就要继续从开始发现1的下一个位置开始继续遍历,由于我们把感染的元素都变为了2,所以就不再去调用infect感染方法了。
直到找到一个未感染(元素都为1)的新岛。依次遍历,当数组完成一次遍历,所有的岛都会被感染,就会找到所有的岛。
时间复杂度为O(N*M).*/
//递归感染周围元素
infect(m, i + 1, j, N, M);//上
infect(m, i - 1, j, N, M);//下
infect(m, i, j - 1, N, M);//作
infect(m, i, j + 1, N, M);//右
}
public static void main(String[] args) {
int[][] m1 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, };
System.out.println(countIslands(m1));
int[][] m2 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 1, 0, 0, 0, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, };
System.out.println(countIslands(m2));
}
}
具体思路代码中注释写的很清楚,主要步骤是:
1 遍历二维数组
2 找到为1的元素标记
3递归感染周围为1的元素
是不是很爽,只有O(N*M)的时间复杂度。。。
并行计算,会使运行速度极大提高!至于如何使用并行计算,有以下思路,代码还没实现:
比如2个处理器并行计算,就把这个组分为2块,运用并查集的思想(大家可以细细研究下)和上述查找方法,并行查找。
最后在把边界公共的小岛(图中所示的红圈部分)合并就ok了。
如下图所示: