一、二维数组岛屿
1、问题描述
假设一个二维数组,完全有0和1两个数字组成,假设将所有的0认为是海洋,单独的数字1或者连成一片的数字1认为是岛屿,那么求整个二维数组中有多少岛屿,并且打印每个岛屿中的坐标点。
{0,1,0,1,1,0,1,0},
{1,0,0,0,1,0,0,0},
{0,0,1,0,0,1,0,1},
{0,1,0,1,0,0,1,0},
{0,1,1,1,1,0,1,0},
{0,0,1,0,1,0,1,0},
首先,我们可以将数组看做一个围棋棋盘,0或者空位认为是海洋,以下面为例。
2、关于围棋
围棋,一种策略性两人棋类游戏,中国古时称“弈”,西方名称“Go”。流行于东亚国家(中、日、韩、朝),属琴棋书画四艺之一。围棋起源于中国,传为帝尧所作,春秋战国时期即有记载。隋唐时经朝鲜传入日本,流传到欧美各国。围棋蕴含着中华文化的丰富内涵,它是中国文化与文明的体现。
所以,作为中国人,传统很重要,特别是人工智能火热的时代,不学点围棋知识怎么行呢?
二、算法实现
算法原理:
map区域集合法,按顺序遍历,map key值为区号,value 为坐标集合。
- 发现1,找出坐标去每个区查询,找出能连通该坐标的所有区,合并区域到最小key值集合中,并将当前加入该坐标集合中。
- 未发现1,给1创建一个新区,将1的坐标存入该区集合中
1、核心方法
public static class Node {
int x;
int y;
@Override
public String toString() {
return "{" +
"x=" + x +
", y=" + y +
'}';
}
}
public static void printIsLands(int[][] inputMatrix){
if(inputMatrix==null) return;
int ROW = inputMatrix.length;
int COL = inputMatrix[0].length;
final Map<String, Set<Node>> zoneMapList = new HashMap<>();
for (int i=0;i<ROW;i++){
for (int j=0;j<COL;j++){
int val = inputMatrix[i][j];
if(val==0){
continue;
}
Set<String> mapKeys = findLinkableZoneMapList(zoneMapList,i,j);
if(!mapKeys.isEmpty()){
combineMaps(zoneMapList,mapKeys,i,j);
}else{
Node node = new Node();
node.x = i;
node.y = j;
Set<Node> nodes = new HashSet<>();
nodes.add(node);
zoneMapList.put(makeKey(node),nodes);
}
}
}
for (Map.Entry<String,Set<Node>> entry : zoneMapList.entrySet()){
String name = entry.getKey();
Set<Node> nodes = entry.getValue();
System.out.println(name+"=" + nodes);
}
}
private static String makeKey(Node node) {
return node.x+"_"+node.y;
}
private static void combineMaps(Map<String, Set<Node>> zoneMapList, Set<String> mapKeys, int x, int y) {
Set<Node> setNode = new HashSet<>();
Iterator<String> keysIterator = mapKeys.iterator();
String firstKey = null;
do{
if(!keysIterator.hasNext()) break;
String key = keysIterator.next();
if(key==null) continue;
if(firstKey==null){
firstKey = key;
}
if(zoneMapList.containsKey(key)) {
setNode.addAll(zoneMapList.remove(key));
}
}while (true);
Node n = new Node();
n.x = x;
n.y = y;
setNode.add(n);
zoneMapList.put(firstKey,setNode);
}
private static Set<String> findLinkableZoneMapList(Map<String, Set<Node>> zoneMapList, int x, int y) {
Set<String> mapKeys = new HashSet<>();
for (Map.Entry<String,Set<Node>> entry : zoneMapList.entrySet()){
Set<Node> nodes = entry.getValue();
String key = entry.getKey();
if(nodes==null || nodes.isEmpty()) continue;
Iterator<Node> iterator = nodes.iterator();
do{
if(!iterator.hasNext()){
break;
}
Node node = iterator.next();
if(node==null){
iterator.remove();
continue;
}
if(node.x==(x-1) && node.y==(y)){
mapKeys.add(key);
break;
}
if(node.x==(x+1) && node.y==(y)){
mapKeys.add(key);
break;
}
if(node.x==(x) && node.y==(y-1)){
mapKeys.add(key);
break;
}
if(node.x==(x) && node.y==(y+1)){
mapKeys.add(key);
break;
}
}while (true);
}
return mapKeys;
}
三、测试
public class SecretIsland {
public static void main(String[] args) {
int[][] islandMap = {
{0,1,0,1,1,0,1,0},
{1,0,0,0,1,0,0,0},
{0,0,1,0,0,1,0,1},
{0,1,0,1,0,0,1,0},
{0,1,1,1,1,0,1,0},
{0,0,1,0,1,0,1,0},
};
Map<Integer, Point[]> zoneMap = findIsland(islandMap);
printIsland(zoneMap);
}
}
打印结果如下
0_1=[{x=0, y=1}]
1_0=[{x=1, y=0}]
0_3=[{x=0, y=4}, {x=0, y=3}, {x=1, y=4}]
2_2=[{x=2, y=2}]
3_1=[{x=4, y=4}, {x=5, y=4}, {x=4, y=3}, {x=5, y=2}, {x=4, y=2}, {x=3, y=1}, {x=4, y=1}, {x=3, y=3}]
0_6=[{x=0, y=6}]
2_5=[{x=2, y=5}]
2_7=[{x=2, y=7}]
3_6=[{x=3, y=6}, {x=5, y=6}, {x=4, y=6}]
可见,该矩阵有9个孤岛
四、递归算法
public static int getIslandMatrix(int[][] matrix) {
if (matrix == null) {
return 0;
}
int ROW = matrix.length;
if (ROW == 0) {
return 0;
}
int COL = matrix[0].length;
int N = 0;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
int val = matrix[i][j];
if (val != 1) {
continue;
}
N++;
DFS(matrix, i, j);
}
}
return N;
}
private static void DFS(int[][] matrix, int i, int j) {
if (i < 0 || j < 0) {
return;
}
if (i >= matrix.length) {
return;
}
if (j >= matrix[0].length) {
return;
}
if (matrix[i][j] != 1) {
return;
}
matrix[i][j] = 2;
DFS(matrix, i - 1, j);
DFS(matrix, i + 1, j);
DFS(matrix, i, j + 1);
DFS(matrix, i, j - 1);
}
递归改进法,大量数据处理时,使用递归容易造成StackOverflowError,因此我们可以进行一些改进,我们知道,递归的改进往往需要栈来完成
public static int getIslandMatrix(int[][] matrix) {
if (matrix == null) {
return 0;
}
int ROW = matrix.length;
if (ROW == 0) {
return 0;
}
int COL = matrix[0].length;
int N = 0;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
int val = matrix[i][j];
if (val != 1) {
continue;
}
N++;
DFS(matrix, i, j);
}
}
return N;
}
private static void DFS(int[][] matrix, int i, int j) {
Stack<Point> stacks = new Stack<>();
stacks.push(new Point(i,j));
while (!stacks.empty()){
Point point = stacks.pop();
int x = point.x;
int y = point.y;
if (x < 0 || y < 0) {
continue;
}
if (x >= matrix.length) {
continue;
}
if (y >= matrix[0].length) {
continue;
}
if (matrix[x][y] != 1) {
continue;
}
matrix[x][y] = 2;
stacks.push(new Point(x - 1,y));
stacks.push(new Point(x + 1,y));
stacks.push(new Point(x ,y+1));
stacks.push(new Point(x ,y-1));
}
}