问题
输入是以1和0表示的一张地图,其中1代表陆地,0代表海洋。岛屿是1上下左右相连通的区域。(不包括对角线)输出是岛屿的个数。可以认为地图的周围都是海洋。
这是今天字节跳动笔试遇到的一道题。其实质是求一张位图的连通域。同时也出现在滴滴2017年春招算法工程师笔试题b卷中。牛客网上大多数人是用解法1做的。解法2应该是用并查集,先挖个坑以后填上。
此题的另一个版本要求同时输出最大区域的面积(1的个数);输入先给出两个整数作为数组的长和宽,各元素之间有空格隔开。本题的输入是直接给出数组内容且元素之间没有空格,读取输入并获得数组的维度有一定技巧。
输入示例
11000
11000
00100
00011
输出
3
解法1 - 传统解法
思路:
数据结构:二维数组,元素是点Node,Node只有一个布尔属性island,标识这个点是否是陆地。
算法:先读取输入并获得地图的长和宽。之后遍历二维数组,若发现点为1,则岛屿数+1,并调用函数递归把这个点和包含这个点的连通域中的全部点都修改为0。也就是发现一个连通域后,岛屿计数器groups自增,然后抹去这个连通域。遍历完成后计数器groups的值即为岛屿数。时间复杂度为O(n*m),n, m是数组的长和宽。
#include <cstdio>
#include <cstring>
using namespace std;
typedef struct node{
bool island;
} Node;
int row = 0, col = 0;
Node map[1000][1000];
void mark_area(int i, int j){
/* 右方的节点 */
if(j+1 < col && map[i][j+1].island){
map[i][j+1].island = false;
mark_area(i, j+1);
}
/* 下方的节点 */
if(i+1 < row && map[i+1][j].island){
map[i+1][j].island = false;
mark_area(i+1, j);
}
/* 左方的节点 */
if(j-1 >=0 && map[i][j-1].island){
map[i][j-1].island = false;
mark_area(i, j-1);
}
/* 上方的节点 */
if(i-1 >=0 && map[i-1][j].island){
map[i-1][j].island = false;
mark_area(i-1, j);
}
return;
}
int main(int argc, char **argv){
/* 数组清零 */
memset(map, 0, sizeof(Node) * 1e6);
/* 输入读取 */
char n;
int i = 0, j = 0;
while(true){
if(scanf("%c", &n) == EOF){
row = i + 1;
goto MARK;
}
else if(n == '\n' || n == '\r'){
i++;
col = j;
j = 0;
}
else{
map[i][j].island = (bool)(n - '0');
j++;
}
} // end of while loop
MARK:
/* 打印内容 */
printf("row = %d, col = %d\n", row, col);
for(i = 0; i < row; i++){
for(j = 0; j < col ;j++){
printf("%d ", map[i][j].island);
}
printf("\n");
}
/* 计算岛屿数 */
int groups = 0;
for(i = 0; i < row; i++){
for(j = 0; j < col ;j++){
if(map[i][j].island == true){
groups ++;
map[i][j].island = false;
mark_area(i, j);
}
}
}
printf("%d\n", groups);
return 0;
}
解法2
todo
版本日志
- 2018/12/12 第一次发布