题目:海岛面积计算题,给一个矩阵,0表示还睡,相连的1表示海岛,上下左右表示相连。
要求:
(0)求有几座岛
(1)求最大海岛的面积
(2)求最大海岛的面积和对应海岛的所有坐标
(3)求所有海岛的所有坐标,按海岛分。
这道题首先就是要求海岛,我们可以用一个感染思想,当读到的坐标为1的时候,就把他改为2,然后以这个坐标做递归,把他周围所有的1都改为2,然后计数器+1,就能把第一题解决了。
我们设计一个结构,有areas代表海岛面积,每次把1修改为2的时候+1;index代表这一座岛的编号证明是同一座岛;islandIndex为一个String的list,存于存储海岛的坐标。有了这个结构,后三道题也就解决了。
import java.util.ArrayList;
import java.util.List;
public class Island {
static class IslandNode {
public IslandNode(int areas) {
this.areas = areas;
this.index = 0 + "";
}
@Override
public String toString() {
return areas + "-" + index;
}
int areas;
String index;
List<String> islandIndex = new ArrayList<>();
}
static List<IslandNode> list = new ArrayList<IslandNode>();
public static int countIslands(int[][] arr) {
int row = arr.length;// hang
int col = arr[0].length;// lie
int res = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (arr[i][j] == 1) {
res++;
IslandNode node = new IslandNode(0);
infaction(arr, i, j, row, col, node);
}
}
}
return res;
}
private static void infaction(int[][] arr, int i, int j, int row, int col, IslandNode node) {
if (i < 0 || j < 0 || i >= row || j >= col || arr[i][j] != 1) {
return;
}
arr[i][j] = 2;
// 海岛面积+1
node.areas++;
// 保存海岛的坐标
node.islandIndex.add("(" + i + "," + j + ")");
// 判断这个海岛是否是第一次找到,我们初始海岛标记为0的字符串
if (node.index.equals("0")) {
// 修改海岛坐标,根据你数据量可以调整随机数的范围,这里取值范围是1-10
node.index = (int) (Math.random() * 10 + 1) + "";
// 将这个海岛的相应数据保存到一个全局list
list.add(node);
}
infaction(arr, i + 1, j, row, col, node);
infaction(arr, i - 1, j, row, col, node);
infaction(arr, i, j + 1, row, col, node);
infaction(arr, i, j - 1, row, col, node);
}
/**
* 获得最大岛面积
*
* @param nodes
* @return
*/
public static int maxIslandAreas(List<IslandNode> nodes) {
int maxx = Integer.MIN_VALUE;
for (IslandNode islandNode : nodes) {
if (islandNode.areas > maxx) {
maxx = islandNode.areas;
}
}
return maxx;
}
/**
* 获得最大岛面积和对应坐标
*
* @param nodes
* @return
*/
public static String maxIsland(List<IslandNode> nodes) {
int maxx = Integer.MIN_VALUE;
List<String> areaIndex = null;
for (IslandNode islandNode : nodes) {
if (islandNode.areas > maxx) {
maxx = islandNode.areas;
areaIndex = islandNode.islandIndex;
}
}
return "最大岛面积:" + maxx + ",对应坐标:" + areaIndex;
}
/**
* 获得所有岛和岛面积
*
* @param nodes
*/
public static void getIslands(List<IslandNode> nodes) {
for (IslandNode islandNode : nodes) {
System.out.println("岛面积:" + islandNode.areas + ",岛坐标:" + islandNode.islandIndex);
}
}
public static void main(String[] args) {
int[][] m1 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0
{ 0, 1, 1, 1, 0, 1, 1, 1, 0 }, // 1
{ 0, 1, 1, 1, 0, 0, 0, 1, 0 }, // 2
{ 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // 3
{ 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 4
{ 0, 0, 0, 0, 1, 1, 1, 0, 0 }, // 5
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, };// 6
System.out.println("==================第零题==================");
System.out.println("一共有:" + countIslands(m1) + "座岛");
System.out.println("==================第一题==================");
System.out.println("最大岛面积:" + maxIslandAreas(list));
System.out.println("==================第二题==================");
System.out.println(maxIsland(list));
System.out.println("==================第三题==================");
getIslands(list);
}
}
对应输出如下:
==================第零题==================
一共有:3座岛
==================第一题==================
最大岛面积:8
==================第二题==================
最大岛面积:8,对应坐标:[(1,1), (2,1), (3,1), (3,2), (2,2), (1,2), (1,3), (2,3)]
==================第三题==================
岛面积:8,岛坐标:[(1,1), (2,1), (3,1), (3,2), (2,2), (1,2), (1,3), (2,3)]
岛面积:4,岛坐标:[(1,5), (1,6), (1,7), (2,7)]
岛面积:5,岛坐标:[(4,5), (5,5), (5,6), (4,6), (5,4)]
进阶
题目不变,要求变为用多台机器来解决这个问题,那么最主要的问题就在于怎么合并每台机器的数据了。
分治思想,首先还是用感染思想,先找到每个板块的岛数量,并且每一座岛需要有一个唯一标识来区别。然后合并的时候,就只需要看边界,我们先以两台机器为例。
如上图,一个颜色代表一座岛,首先各自找到自己的数据中有几座岛,左边区域有5座岛,右边区域有1座岛,都有自己的唯一标识(这里假设用ABCDEF标识)。
然后就开始比较边界,这里只需要比较左区域的右边界,右区域的左边界。从上往下找,找到左区域的第一个1,并读取标识,然后去右区域找第一个1读取标识。发现他们是连着的,那么记录一下他们的标识,然后岛的数量-1,然后往下找,当找到B的时候,发现右区域的D连着,那么总岛数-1,再继续往下看到C ,并没有相连,所以继续往下走。所以最后得出一共有4座岛。
在比较边界的时候,第一次AD查重的时候数量-1了,如果往下找,发现还是AD相连,就不用再-1了,就像下图这种情况。也就是说我们需要把AD放到一个集合里面,每次需要做一个判断来确定是否-1。
这种题可以用一种叫做并查集的结构来处理,会相当方便。后面再补并查集的说明吧