二维数组寻找小岛

有这样一道题:


是不是看起来很复杂,昨天听了左神讲的课,他用了一个简单的算法,时间复杂度只有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了。
如下图所示:



  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值