
<p align="center"><strong><a href="./qita/join.md">参与本项目</a>，贡献其他语言版本的代码，拥抱开源，让更多学习算法的小伙伴们受益！</strong></p>

# 106. 岛屿的周长 

[卡码网题目链接（ACM模式）](https://kamacoder.com/problempage.php?pid=1178)

题目描述

给定一个由 1（陆地）和 0（水）组成的矩阵，岛屿是被水包围，并且通过水平方向或垂直方向上相邻的陆地连接而成的。


你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿，假设组成岛屿的陆地边长都为 1，请计算岛屿的周长。岛屿内部没有水域。

输入描述

第一行包含两个整数 N, M，表示矩阵的行数和列数。之后 N 行，每行包含 M 个数字，数字为 1 或者 0，表示岛屿的单元格。

输出描述

输出一个整数，表示岛屿的周长。

输入示例

```
5 5
0 0 0 0 0
0 1 0 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
```

输出示例

14

提示信息 

![](https://file1.kamacoder.com/i/algo/20240524115244.png) 

岛屿的周长为 14。

数据范围：

1 <= M, N <= 50。

## 思路

岛屿问题最容易让人想到BFS或者DFS，但本题确实还用不上。 

为了避免大家惯性思维，所以给大家安排了这道题目。 

### 解法一：

遍历每一个空格，遇到岛屿则计算其上下左右的空格情况。

如果该陆地上下左右的空格是有水域，则说明是一条边，如图：  

![](https://file1.kamacoder.com/i/algo/20240524115933.png) 

陆地的右边空格是水域，则说明找到一条边。 


如果该陆地上下左右的空格出界了，则说明是一条边，如图：   

![](https://file1.kamacoder.com/i/algo/20240524120105.png) 

该陆地的下边空格出界了，则说明找到一条边。 


C++代码如下：（详细注释）

```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                for (int k = 0; k < 4; k++) {       // 上下左右四个方向
                    int x = i + direction[k][0];
                    int y = j + direction[k][1];    // 计算周边坐标x,y
                    if (x < 0                       // x在边界上
                            || x >= grid.size()     // x在边界上
                            || y < 0                // y在边界上
                            || y >= grid[0].size()  // y在边界上
                            || grid[x][y] == 0) {   // x,y位置是水域
                        result++;
                    }
                }
            }
        }
    }
    cout << result << endl;

}
```

### 解法二：

计算出总的岛屿数量，总的变数为：岛屿数量 * 4

因为有一对相邻两个陆地，边的总数就要减2，如图红线部分，有两个陆地相邻，总边数就要减2

![](https://file1.kamacoder.com/i/algo/20240524120855.png)

那么只需要在计算出相邻岛屿的数量就可以了，相邻岛屿数量为cover。

结果 result = 岛屿数量 * 4 - cover * 2;

C++代码如下：（详细注释）

```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    int sum = 0;    // 陆地数量
    int cover = 0;  // 相邻数量
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                sum++; // 统计总的陆地数量
                // 统计上边相邻陆地
                if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
                // 统计左边相邻陆地
                if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
                // 为什么没统计下边和右边？ 因为避免重复计算
            }
        }
    }

    cout << sum * 4 - cover * 2 << endl;

}
```

## 其他语言版本

### Java
```Java
import java.util.*;

public class Main {
    // 每次遍历到1，探索其周围4个方向，并记录周长，最终合计
    // 声明全局变量，dirs表示4个方向
    static int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    // 统计每单个1的周长
    static int count;
    
    // 探索其周围4个方向，并记录周长
    public static void helper(int[][] grid, int x, int y) {
        for (int[] dir : dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            
            // 遇到边界或者水，周长加一
            if (nx < 0 || nx >= grid.length || ny < 0 || ny >= grid[0].length
                || grid[nx][ny] == 0) {
                count++;
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 接收输入
        int M = sc.nextInt();
        int N = sc.nextInt();

        int[][] grid = new int[M][N];
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                grid[i][j] = sc.nextInt();
            }
        }

        int result = 0; // 总周长
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                if (grid[i][j] == 1) {
                    count = 0;
                    helper(grid, i, j);
                    // 更新总周长
                    result += count;
                }
            }
        }
        
        // 打印结果
        System.out.println(result);
    }
}
```

### Python

```python

def main():
    import sys
    input = sys.stdin.read
    data = input().split()
    
    # 读取 n 和 m
    n = int(data[0])
    m = int(data[1])
    
    # 初始化 grid
    grid = []
    index = 2
    for i in range(n):
        grid.append([int(data[index + j]) for j in range(m)])
        index += m
    
    sum_land = 0    # 陆地数量
    cover = 0       # 相邻数量

    for i in range(n):
        for j in range(m):
            if grid[i][j] == 1:
                sum_land += 1
                # 统计上边相邻陆地
                if i - 1 >= 0 and grid[i - 1][j] == 1:
                    cover += 1
                # 统计左边相邻陆地
                if j - 1 >= 0 and grid[i][j - 1] == 1:
                    cover += 1
                # 不统计下边和右边，避免重复计算
    
    result = sum_land * 4 - cover * 2
    print(result)

if __name__ == "__main__":
    main()


```

### Go

```go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	line := scanner.Text()
	
	n, m := parseInput(line)

	// 初始化 grid
	grid := make([][]int, n)
	for i := range grid {
		grid[i] = make([]int, m)
	}

	// 读入 grid 数据
	for i := 0; i < n; i++ {
		scanner.Scan()
		line := scanner.Text()
		values := parseLine(line, m)
		for j := 0; j < m; j++ {
			grid[i][j] = values[j]
		}
	}

	sum := 0   // 陆地数量
	cover := 0 // 相邻数量

	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if grid[i][j] == 1 {
				sum++ // 统计总的陆地数量

				// 统计上边相邻陆地
				if i-1 >= 0 && grid[i-1][j] == 1 {
					cover++
				}
				// 统计左边相邻陆地
				if j-1 >= 0 && grid[i][j-1] == 1 {
					cover++
				}
				// 为什么没统计下边和右边？ 因为避免重复计算
			}
		}
	}

	fmt.Println(sum*4 - cover*2)
}

// parseInput 解析 n 和 m
func parseInput(line string) (int, int) {
	parts := strings.Split(line, " ")
	n, _ := strconv.Atoi(parts[0])
	m, _ := strconv.Atoi(parts[1])
	return n, m
}

// parseLine 解析一行中的多个值
func parseLine(line string, count int) []int {
	parts := strings.Split(line, " ")
	values := make([]int, count)
	for i := 0; i < count; i++ {
		values[i], _ = strconv.Atoi(parts[i])
	}
	return values
}


```


### Rust

### JavaScript

### TypeScript

### PhP

### Swift

### Scala

### C#

### Dart

### C

<div align="center"><a href="https://www.nowcoder.com/link/wx_sqlcarl199" target="_blank"><img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20220416223343.png" width="1000"/></a></div>
