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. Kubernetes集群部署DNS插件

    准备 kube-dns 相关镜像 准备 kube-dns 相关 yaml 文件 系统预定义的 RoleBinding 配置 kube-dns 相关服务 检查 kube-dns 功能 kube-dns ...

  2. Scheme实现数字电路仿真(3)——模块

    版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/12242650.html 作者:窗户 ...

  3. web资源预加载-生产环境实践

    此文记录资源预加载在我们项目的实践,技术难度不算高,重在介绍一套技术方案的诞生与实施,其中都进行了哪些思考,依据什么来做决策,如何进行效果评估,等等.为读者在制定技术方案时提供一定启示. 背景 资源预 ...

  4. SpringBoot2整合Redis缓存

    遵循SpringBoot三板斧 第一步加依赖 <!-- Redis --> <dependency> <groupId>org.springframework.bo ...

  5. 一个简单的爬取b站up下所有视频的所有评论信息的爬虫

    心血来潮搞了一个简单的爬虫,主要是想知道某个人的b站账号,但是你知道,b站在搜索一个用户时,如果这个用户没有投过稿,是搜不到的,,,这时就只能想方法搞到对方的mid,,就是 space.bilibil ...

  6. win10下LoadRunner12 下载安装图文教程

    1.下载安装包: 链接:https://pan.baidu.com/s/1hiGC9FjfKOFRWHVfMAHaeg 提取码:sr8x 如下图所示,咱们直接安装社区版“HP_LoadRunner_1 ...

  7. 1,Java知识储备

    1,关于 . java文件 规定:第一行为 package name; 表示该.java文件属于哪一个包. 一个.java文件中可以有多个类,但是只能有一个public类,并且这个public类必须与 ...

  8. IAR软件使用的快捷键配置以及配置cc2530环境

    以下是我对IAR软件使用的快捷键配置cc2530以及配置环境的总结,如下图所示 ​ ​ ​ 弹出保存窗口 ​ 工程生成完毕——生成.c文件 ​ 快捷键ctrl+s保存.c文件 ​ ​ ​ ​ ​ 选择 ...

  9. python自动化第二课 - python基础

    1.标识符(identitifier),识别身份 定义:在编程语言中,标识符就是用户编程时使用的名字,用于给变量.常量.函数.语句块等命名 标识符命令规范: 1)英文,数字以及下划线(_)但不能以数字 ...

  10. 以正确的方式下载和配置 ASP.NET Core 官方源码

    我们可以在Github上面直接查看ASP.NET Core 3.x的源代码,但是我们也可以把源代码下载下来进行查看. 而下载源代码进行查看有很多好处: 任意的导航源代码 内置了一个示例项目 直接调试源 ...