岛屿周长问题的三种解法:直接计数法、数学计算法与深度优先搜索

问题描述

给定一个二维网格 grid,其中1表示陆地,0表示水域。网格中的格子水平和垂直方向相连(对角线不相连)。网格中恰好有一个岛屿(即一个或多个相连的陆地格子),需要计算这个岛屿的周长。

解法一:直接计数法(迭代法)

思路分析

这是最直观的解法:遍历网格中的每个格子,如果是陆地,初始周长为4。然后检查其上下左右四个方向的相邻格子:

  • 每有一个相邻的陆地格子,周长减1

  • 边界情况自动处理(边界外的格子视为水域)

代码实现

cpp 复制代码
class Solution {
public:
    int islandPerimeter(vector<vector<int>>& grid) {
        int res = 0;
        int m = grid.size(), n = grid[0].size();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                int add = 4;    // 方格初始周长
                if(grid[i][j] == 1) {
                    if(i - 1 >= 0 && grid[i - 1][j] == 1) add--; // 上
                    if(i + 1 < m && grid[i + 1][j] == 1) add--;  // 下
                    if(j - 1 >= 0 && grid[i][j - 1] == 1) add--; // 左
                    if(j + 1 < n && grid[i][j + 1] == 1) add--;  // 右
                    res += add;
                }
            }
        }
        return res;
    }
};

复杂度分析

  • 时间复杂度:O(mn),其中m和n分别是网格的行数和列数

  • 空间复杂度:O(1),仅使用常数级别的额外空间

解法二:数学计算法

思路分析

这种方法基于一个数学观察:

  1. 每个陆地格子贡献4条边

  2. 每对相邻的陆地格子共享一条边,会使总周长减少2条边

因此,岛屿周长 = 陆地格子数 × 4 - 相邻边数 × 2

代码实现

java 复制代码
class Solution {
    public int islandPerimeter(int[][] grid) {
        int landCount = 0; // 陆地格子数量
        int adjacentCount = 0; // 相邻边数量
        
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == 1) {
                    landCount++;
                    // 只检查右侧和下侧,避免重复计数
                    if (c + 1 < grid[0].length && grid[r][c + 1] == 1) {
                        adjacentCount++;
                    }
                    if (r + 1 < grid.length && grid[r + 1][c] == 1) {
                        adjacentCount++;
                    }
                }
            }
        }
        return landCount * 4 - adjacentCount * 2;
    }
}

复杂度分析

  • 时间复杂度:O(mn)

  • 空间复杂度:O(1)

解法三:深度优先搜索(DFS)

思路分析

使用深度优先搜索遍历岛屿:

  1. 当从陆地移动到边界或水域时,周长增加1

  2. 使用标记避免重复访问(将访问过的陆地标记为2)

  3. 递归探索四个方向(右、下、左、上)

代码实现

cpp 复制代码
class Solution {
    constexpr static int dx[4] = {0, 1, 0, -1}; // 右、下、左、上
    constexpr static int dy[4] = {1, 0, -1, 0};
    
public:
    int dfs(int x, int y, vector<vector<int>> &grid, int n, int m) {
        // 遇到边界或水域,贡献1条边
        if (x < 0 || x >= n || y < 0 || y >= m || grid[x][y] == 0) {
            return 1;
        }
        // 已访问过的陆地,不贡献边
        if (grid[x][y] == 2) {
            return 0;
        }
        grid[x][y] = 2; // 标记为已访问
        int res = 0;
        // 探索四个方向
        for (int i = 0; i < 4; ++i) {
            int tx = x + dx[i];
            int ty = y + dy[i];
            res += dfs(tx, ty, grid, n, m);
        }
        return res;
    }
    
    int islandPerimeter(vector<vector<int>> &grid) {
        int n = grid.size(), m = grid[0].size();
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == 1) {
                    ans += dfs(i, j, grid, n, m);
                }
            }
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(mn),每个格子最多访问一次

  • 空间复杂度:O(mn),递归调用栈的深度在最坏情况下可能达到网格大小

总结与对比

方法 优点 缺点 适用场景
直接计数法 直观易懂,实现简单 需要检查四个方向 小规模网格
数学计算法 效率高,仅需遍历一次 需要理解数学原理 所有规模网格
深度优先搜索 符合岛屿遍历的直觉,可扩展性强 需要递归,可能栈溢出 大规模连通岛屿或需要扩展功能

在实际应用中:

  1. 对于简单场景,数学计算法通常是最优选择,高效且简洁

  2. 当需要扩展功能(如计算多个岛屿)时,DFS更具扩展性

  3. 直接计数法则提供了最直观的实现参考

理解这三种解法的核心思想,能够帮助我们在解决类似网格问题时灵活选择最合适的策略。

相关推荐
tanxiaomi3 分钟前
Redisson分布式锁 和 乐观锁的使用场景
java·分布式·mysql·面试
零匠学堂20254 分钟前
移动学习系统,如何提升企业培训效果?
java·开发语言·spring boot·学习·音视频
不会c嘎嘎7 分钟前
【数据结构】AVL树详解:从原理到C++实现
数据结构·c++
小杨快跑~11 分钟前
从装饰者到桥接再到工厂:模式组合的艺术
java·开发语言·设计模式
饕餮争锋14 分钟前
Spring内置的Bean作用域介绍
java·后端·spring
却话巴山夜雨时i14 分钟前
394. 字符串解码【中等】
java·数据结构·算法·leetcode
haing201915 分钟前
使用黄金分割法计算Bezier曲线曲率极值的方法介绍
算法·黄金分割
leoufung21 分钟前
LeetCode 230:二叉搜索树中第 K 小的元素 —— 从 Inorder 遍历到 Order Statistic Tree
算法·leetcode·职场和发展
jyyyx的算法博客24 分钟前
多模字符串匹配算法 -- 面试题 17.17. 多次搜索
算法
da_vinci_x25 分钟前
Sampler AI + 滤波算法:解决 AIGC 贴图“噪点过剩”,构建风格化 PBR 工业管线
人工智能·算法·aigc·材质·贴图·技术美术·游戏美术