图-连通分量-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 ...
随机推荐
- Redis过期key淘汰策略
Redis采用惰性+定期的key淘汰策略 1. Redis配置项hz定义了serverCron任务的执行周期,默认为10,即CPU空闲时每秒执行10次; 2. 每次过期key清理的时间不超过CPU时间 ...
- 一致性 Hash 算法分析
当我们在做数据库分库分表或者是分布式缓存时,不可避免的都会遇到一个问题: 如何将数据均匀的分散到各个节点中,并且尽量的在加减节点时能使受影响的数据最少. Hash 取模 随机放置就不说了,会带来很多问 ...
- Qt 隐藏标题栏 窗口移动 鼠标事件
摘要 隐藏标题栏 头文件声明鼠标移动虚函数 .cpp文件实现功能 1 setWindowFlags(Qt::FramelessWindowHint | windowFlags()); 无标题栏移动窗体 ...
- 脸书VS微软,为何“老年创业者”更担忧AI失控?
作为互联网行业最知名的大会之一,近日举行的微软Build 2017大会,却增加了与以往不同的"调味品".除了新技术.智能硬件.系统.平台之外,微软CEO纳德拉在大会上对科技带给人类 ...
- SpringMVC 使用注解完成登录拦截
目录 为了实现用户登录拦截你是否写过如下代码呢? 1. 基于Filter 2. 基于Struts 3. 基于SpringMVC 如何使用自定义注解完成自定义拦截呢? 登录注解 SpringMVC 拦截 ...
- C++走向远洋——50(Time类中的运算符重载、一目,二目比较运算符、二目赋值运算符、二目加减法运算符)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- 参考C# 使用 System.Web.Script.Serialization 解析 JSON
参考C# 使用 System.Web.Script.Serialization 解析 JSON 使用json需要引用到System.Web.Script.Serialization.习惯在解决方案右键 ...
- 强大的java工作流引擎,可视化开发工作流
我们先来看看什么是工作流? 所谓工作流引擎是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色.分工和条件的不同决定信息传递路由.内容等级等核心解决方案.工作流引擎包括 ...
- canvas初尝试
最近学习了canvas,就拿它做了这么个小东西,感觉已经爱上canvas了.上代码 /* * @auhor : 开发部-前端组-李鑫超 * @property { tableData : {Array ...
- Flink系列之1.10版流式SQL应用
随着Flink 1.10的发布,对SQL的支持也非常强大.Flink 还提供了 MySql, Hive,ES, Kafka等连接器Connector,所以使用起来非常方便. 接下来咱们针对构建流式SQ ...