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. python3下BeautifulSoup练习一(爬取小说)

    上次写博客还是两个月以前的事,今天闲来无事,决定把以前刚接触python爬虫时的一个想法付诸行动:就是从网站上爬取小说,这样可以省下好多流量(^_^). 因为只是闲暇之余写的,还望各位看官海涵:不足之 ...

  2. Spring Boot 学习1-创建Spring Boot应用

    如果使用Maven, 确保先安装好Maven再继续. 创建POM文件 在这里有两种方式: 继承Spring Boot parent的pom. 不继承. 继承Spring Boot pom 1 2 3 ...

  3. 一致性哈希算法(consistent hashing)PHP实现

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希 ...

  4. C#使用正则表达式获取HTML代码中a标签里包含指定后缀的href的值

    //C#使用正则表达式获取HTML代码中a标签里包含指定后缀的href的值,表达式如下: Regex regImg = new Regex(@"(?is)<a[^>]*?href ...

  5. 大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)

    大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)   现在大部分的Spring项目都采用了基于注解的配置,采用了@Configuration 替换标签的做法.一 ...

  6. 如何用Postman做接口自动化测试

    目录 前言 什么是自动化测试 自动测试测试分类 为什么需要自动化测试 Postman自动化测试演示 1.新建集合 2.新建接口 3.填写自动化测试脚本 4.录入所有接口 5.执行自动化测试 前言 什么 ...

  7. 一起了解 .Net Foundation 项目 No.12

    .Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. Cecil Cecil 是 ...

  8. 一文看懂js中元素的客户区大小(clientWidth,clientHeight)

    元素的客户区 元素的客户区大小,指的是元素内容及其内边距所占据的空间大小. 相关属性如下: 1. clientWidth:元素内容区宽度+元素左右内边距 2. clientHeight:元素内容区高度 ...

  9. Lambda表达式(JDK8)

    在说Lambda表达式之前,先介绍一下函数式接口 函数式接口 就是只定义了一个抽象方法的接口,我们可以使用注解@Functionallnterface,来强约束这种接口为函数式接口.如Runnable ...

  10. 差分放大电路的CMRR与输入电阻分析

    分析了经典差分放大电路的共模抑制比CMRR与输入电阻RIN 1.经典差分放大电路 基于运放的经典差分放大电路在各模电教材中均能找到,利用分离电阻和运算放大器实现,如图1所示为一种差分放大电路: 图1 ...