2020-03-15 16:41:45

问题描述:

给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。

示例 2:

[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0。

注意: 

给定的矩阵grid 的长度和宽度都不超过 50。

相似问题:

749. 隔离病毒

803. 打砖块

问题求解:

解法一:DFS

使用DFS求解可以理解为将整张图中的连通分量进行染色,在染色过程中记录下当前染色的色块数量,最后取最大值即可。

时间复杂度:O(mn)

    int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int color = 1;
int area = 0;
int res = 0;
public int maxAreaOfIsland(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
area = 0;
dfs(grid, i, j, ++color);
res = Math.max(res, area);
}
}
}
return res;
} private void dfs(int[][] grid, int x, int y, int color) {
int m = grid.length;
int n = grid[0].length;
grid[x][y] = color;
area += 1;
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] != 1) continue;
dfs(grid, nx, ny, color);
}
}

解法二:并查集

采用并查集也可以解决这个问题,具体来说,对于每一个还未被连接的陆地,我们需要将其和它的四个邻居进行union,并且在union的时候需要维护这个集合的总的个数。最后返回森林中个数最大的即可。

时间复杂度:O(mn)

    int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
int m = grid.length;
int n = grid[0].length;
int[] parent = new int[m * n];
int[] area = new int[m * n];
for (int i = 0; i < m * n; i++) {
parent[i] = i;
area[i] = 1;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int key = i * n + j;
if (grid[i][j] == 1 && parent[key] == key) {
for (int[] dir : dirs) {
int ni = i + dir[0];
int nj = j + dir[1];
if (ni < 0 || ni >= m || nj < 0 || nj >= n || grid[ni][nj] == 0) continue;
int nk = ni * n + nj;
union(parent, key, nk, area);
}
}
}
}
for (int i = 0; i < m * n; i++) {
if (grid[i / n][i % n] == 1 && parent[i] == i) res = Math.max(res, area[i]);
}
return res;
} private int find(int[] parent, int i) {
if (parent[i] != i) parent[i] = find(parent, parent[i]);
return parent[i];
} private boolean union(int[] parent, int i, int j, int[] area) {
int pi = find(parent, i);
int pj = find(parent, j);
if (pi == pj) return false;
parent[pi] = pj;
area[pj] += area[pi];
return true;
}

 

803. 打砖块

问题描述:

我们有一组包含1和0的网格;其中1表示砖块。 当且仅当一块砖直接连接到网格的顶部,或者它至少有一块相邻(4 个方向之一)砖块不会掉落时,它才不会落下。

我们会依次消除一些砖块。每当我们消除 (i, j) 位置时, 对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这个消除而落下。

返回一个数组表示每次消除操作对应落下的砖块数目。

示例 1:

输入:
grid = [[1,0,0,0],[1,1,1,0]]
hits = [[1,0]]
输出: [2]
解释: 
如果我们消除(1, 0)位置的砖块, 在(1, 1) 和(1, 2) 的砖块会落下。所以我们应该返回2。

示例 2:

输入:
grid = [[1,0,0,0],[1,1,0,0]]
hits = [[1,1],[1,0]]
输出:[0,0]
解释:
当我们消除(1, 0)的砖块时,(1, 1)的砖块已经由于上一步消除而消失了。所以每次消除操作不会造成砖块落下。注意(1, 0)砖块不会记作落下的砖块。

注意:

网格的行数和列数的范围是[1, 200]。
消除的数字不会超过网格的区域。
可以保证每次的消除都不相同,并且位于网格的内部。
一个消除的位置可能没有砖块,如果这样的话,就不会有砖块落下。

问题求解:

解法一:DFS

我们可以使用dfs进行模拟,每次击打砖块后,对其四个邻居进行判断,看其是否会掉落,如果掉落,则将其连通块统一设置为0。

时间复杂度:O(mn*len(hits))

    // leetcode AC; lintcode Fail
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int cnt = 0;
public int[] hitBricks(int[][] grid, int[][] hits) {
int m = grid.length;
int n = grid[0].length;
int k = hits.length;
int[] res = new int[k];
for (int i = 0; i < k; i++) {
int[] hit = hits[i];
int x = hit[0];
int y = hit[1];
if (grid[x][y] == 0) continue;
grid[x][y] = 0;
cnt = 0;
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] == 0) continue;
if (is_fall(grid, nx, ny, new int[m][n])) hit_down(grid, nx, ny);
}
res[i] = cnt;
}
return res;
} private boolean is_fall(int[][] grid, int x, int y, int[][] used) {
int m = grid.length;
int n = grid[0].length;
if (x == 0) return false;
used[x][y] = 1;
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] == 0 || used[nx][ny] == 1) continue;
if (!is_fall(grid, nx, ny, used)) return false;
}
return true;
} private void hit_down(int[][] grid, int x, int y) {
int m = grid.length;
int n = grid[0].length;
grid[x][y] = 0;
cnt += 1;
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] == 0) continue;
hit_down(grid, nx, ny);
}
}

  

解法二:并查集

上述的DFS模拟的算法在lintcode里无法通过,显示递归深度过多。

本题也可以使用并查集求解,主要要考虑到的是如何union:将敲砖块转变成添加砖块,从头开始敲砖块等价于从末尾开始添加砖块,这样就可以使用union操作了。另外,我们还需要一个节点表示所有连接到顶部的集合。

时间复杂度:O(mn * len(hits))

    int[] parent;
int[] size;
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int[] hitBricks(int[][] grid, int[][] hits) {
int[] res = new int[hits.length];
int m = grid.length;
int n = grid[0].length;
parent = new int[m * n + 1];
size = new int[m * n + 1];
for (int i = 0; i <= m * n; i++) {
parent[i] = i;
size[i] = 1;
}
int[][] after = new int[m][n];
for (int i = 0; i < m; i++) after[i] = Arrays.copyOf(grid[i], n);
for (int[] hit : hits) {
int x = hit[0];
int y = hit[1];
after[x][y] = 0;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (after[i][j] == 1) {
for (int[] dir : dirs) {
int ni = i + dir[0];
int nj = j + dir[1];
if (ni < 0 || ni >= m || nj < 0 || nj >= n || after[ni][nj] == 0) continue;
union(i * n + j, ni * n + nj);
}
if (i == 0) union(i * n + j, n * m);
}
}
}
for (int i = hits.length - 1; i >= 0; i--) {
int[] brick = hits[i];
int x = brick[0];
int y = brick[1];
if (grid[x][y] == 0) continue;
after[x][y] = 1;
int prev = size[find(m * n)];
if (x == 0) union(x * n + y, m * n);
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= m || ny < 0 || ny >= n || after[nx][ny] == 0) continue;
union(x * n + y, nx * n + ny);
}
res[i] = Math.max(0, size[find(m * n)] - prev - 1);
}
return res;
} private int find(int i) {
if (parent[i] != i) parent[i] = find(parent[i]);
return parent[i];
} private boolean union(int i, int j) {
int pi = find(i);
int pj = find(j);
if (pi == pj) return false;
parent[pi] = pj;
size[pj] += size[pi];
return true;
}

  

图-连通分量-DFS-并查集-695. 岛屿的最大面积的更多相关文章

  1. [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)

    695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...

  2. Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island)

    Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island) 深度优先搜索的解题详细介绍,点击 给定一个包含了一些 0 和 1的非空二维数组 grid ...

  3. Java实现 LeetCode 695 岛屿的最大面积(DFS)

    695. 岛屿的最大面积 给定一个包含了一些 0 和 1 的非空二维数组 grid . 一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相 ...

  4. Leetcode题目200.岛屿数量(BFS+DFS+并查集-中等)

    题目描述: 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 ...

  5. 1021. Deepest Root (25)——DFS+并查集

    http://pat.zju.edu.cn/contests/pat-a-practise/1021 无环连通图也可以视为一棵树,选定图中任意一点作为根,如果这时候整个树的深度最大,则称其为 deep ...

  6. HDU 5458 Stability(双连通分量+LCA+并查集+树状数组)(2015 ACM/ICPC Asia Regional Shenyang Online)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5458 Problem Description Given an undirected connecte ...

  7. Codeforces 1027D Mouse Hunt (强连通缩点 || DFS+并查集)

    <题目链接> 题目大意: 有n个房间,每个房间都会有一只老鼠.处于第i个房间的老鼠可以逃窜到第ai个房间中.现在要清理掉所有的老鼠,而在第i个房间中防止老鼠夹的花费是ci,问你消灭掉所有老 ...

  8. 洛谷P2542 [AHOI2005]航线规划(LCT,双连通分量,并查集)

    洛谷题目传送门 太弱了不会树剖,觉得LCT好写一些,就上LCT乱搞,当LCT维护双连通分量的练手题好了 正序删边是不好来维护连通性的,于是就像水管局长那样离线处理,逆序完成操作 显然,每个点可以代表一 ...

  9. ZOJ 3811 / 2014 牡丹江赛区网络赛 C. Untrusted Patrol bfs/dfs/并查集

    Untrusted Patrol Time Limit: 3 Seconds                                     Memory Limit: 65536 KB    ...

随机推荐

  1. 在GitHub上分享自己的项目

    GitHub主要是用作基于Git的分布式版本管理系统的库,可以保存和管理自己的代码,而且主要用作代码的合作开发. 注册GitHub后你就会有0.3G的免费空间,不过只能创建公开项目,这也满足代码分享的 ...

  2. Geohash介绍

    Geohash介绍 Geohash是一种地址编码,能把二维的经纬度编码成字符串,某一区域范围内的经纬度是一致的,其中有编码长度控制区域的范围 精度参考 使用场景 实时LBS应用 LBS应用中,搜索某某 ...

  3. 如何在自己的CSDN博客中增添【高大上】的博客栏目?

    前几天看到过一位博主的博客界面,向下看 ☟ (博主对不起啊!把你的公众号给抹了~~~),感觉做这个东西挺好玩的,而且我竟然找不到在哪个地方可以设置!在百度上也没有搜到教程,最后问了一下贺老师知道了入口 ...

  4. Java基础IO流 ,文件读取,由易至难

    最基础的读取文件 import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;imp ...

  5. CSS 学习笔记——CSS Selector

    CSS1 中定义的选择器 类型选择器 用于选择指定类型的元素(其实他就是 html 标签选择器),常见用法如下: body { /*对 body 元素定义样式*/ } body,div { /*同时选 ...

  6. css实现边框动画效果

    最近写了几个页面都用到css动画,以及很多before,after伪类.在此记录一下成果.css边框循环动画,页面效果如下: 1.沿着边框动画的图形使用before,after伪类写的.当时想用切图来 ...

  7. 一篇文章彻底说清JS的深拷贝/浅拷贝

    一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者. 第二类,希望扎实JS基础,将来好去面试官前秀操作的好学者. 写给第一类读者 你只需要一 ...

  8. 2019年高校微信小程序开发大赛学习笔记

    学做小程序(学堂在线笔记)一.传统布局 text-align:center //水平居中 margin-bottom: 60px //设置间距 二.弹性盒子布局 display:flex; flex- ...

  9. Jenkins构建项目帮助文档

    Jenkins构建项目帮助文档 主要步骤 一.配置jdk 1.1.   下载jdk,安装到自己电脑磁盘的Java目录下(比如:D:\Java\jdk). 1.2.   Jdk环境变量的配置: 1. 鼠 ...

  10. python网络协议

    一 互联网的本质 咱们先不说互联网是如何通信的(发送数据,文件等),先用一个经典的例子,给大家说明什么是互联网通信. 现在追溯到八九十年代,当时电话刚刚兴起,还没有手机的概念,只是有线电话,那么此时你 ...