图-连通分量-DFS-并查集-695. 岛屿的最大面积
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。
相似问题:
问题求解:
解法一: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. 岛屿的最大面积的更多相关文章
- [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)
695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...
- Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island)
Leetcode之深度优先搜索(DFS)专题-695. 岛屿的最大面积(Max Area of Island) 深度优先搜索的解题详细介绍,点击 给定一个包含了一些 0 和 1的非空二维数组 grid ...
- Java实现 LeetCode 695 岛屿的最大面积(DFS)
695. 岛屿的最大面积 给定一个包含了一些 0 和 1 的非空二维数组 grid . 一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相 ...
- Leetcode题目200.岛屿数量(BFS+DFS+并查集-中等)
题目描述: 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量.一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的.你可以假设网格的四个边均被水包围. 示例 ...
- 1021. Deepest Root (25)——DFS+并查集
http://pat.zju.edu.cn/contests/pat-a-practise/1021 无环连通图也可以视为一棵树,选定图中任意一点作为根,如果这时候整个树的深度最大,则称其为 deep ...
- 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 ...
- Codeforces 1027D Mouse Hunt (强连通缩点 || DFS+并查集)
<题目链接> 题目大意: 有n个房间,每个房间都会有一只老鼠.处于第i个房间的老鼠可以逃窜到第ai个房间中.现在要清理掉所有的老鼠,而在第i个房间中防止老鼠夹的花费是ci,问你消灭掉所有老 ...
- 洛谷P2542 [AHOI2005]航线规划(LCT,双连通分量,并查集)
洛谷题目传送门 太弱了不会树剖,觉得LCT好写一些,就上LCT乱搞,当LCT维护双连通分量的练手题好了 正序删边是不好来维护连通性的,于是就像水管局长那样离线处理,逆序完成操作 显然,每个点可以代表一 ...
- ZOJ 3811 / 2014 牡丹江赛区网络赛 C. Untrusted Patrol bfs/dfs/并查集
Untrusted Patrol Time Limit: 3 Seconds Memory Limit: 65536 KB ...
随机推荐
- ubuntu 18.04用apt安装mysql-server
mysql5.7安装完成后普通用户不能进mysql 原因:root的plugin被修改成了auth_socket,用密码登陆的plugin应该是mysql_native_password,直接用roo ...
- 记php封装app 广告植入问题
<meta http-equiv="X-Frame-Options" content="DENY"> <script type="t ...
- 达拉草201771010105《面向对象程序设计(java)》第七周学习总结
达拉草201771010105<面向对象程序设计(java)>第七周学习总结 实验七继承附加实验 实验时间 2018-10-11 1.实验目的与要求 (1)进一步理解4个成员访问权限修饰符 ...
- LeetCode--链表2-双指针问题
LeetCode--链表2-双指针问题 思考问题: 判断一个链表是否有环 列举几种情况: graph LR A-->B B-->C C-->D D-->E E-->C g ...
- MySQL占用CPU超过百分之100解决过程
本文转载自: https://www.93bok.com 访问网页504 Gateway Time-out,登陆服务器查看,内存正常,CPU使用率达到了400%,因为是4核,所以到了400%,几乎全部 ...
- php表单提交后再后退 内容则默认清空的解决方法
转载原文地址: http://www.jquerycn.cn/a_14422 在session_start()之后,字符输出之前加上header("Cache-control: privat ...
- 前端每日实战:85# 视频演示如何用纯 CSS 创作一个小球反弹的动画
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/OwWROO 可交互视频 此视频是可 ...
- CSS实现响应式布局
用CSS实现响应式布局 响应式布局感觉很高大上,很难,但实际上只用CSS也能实现响应式布局要用的就是CSS中的没接查询,下面就介绍一下怎么运用: 使用@media 的三种方法 1.直接在CSS文件中使 ...
- 用vue开发一个公众号商城SPA——1.前期准备和写页面
使用vue开发公众号商城 第1篇记录项目准备.搭建,写页面遇到第问题以及总结,持续更新 公司最近接了个商城项目,包括PC端商城.微信公众号网页商城.后台管理系统.这几天在做微信公众号商城,又新接触了很 ...
- 计算机思维的逻辑基础是什么? & 计算思维
l 计算机思维的逻辑基础: 计算机思维是指人们操作计算机时,计算机行使特定功能的运作方式. 逻辑基础则是指支撑事物运作的基本法则. 因而,计算机思维的逻辑基础可以理解为,计算机在行使特定功能时,其运 ...