LeetCode:200. Number of Islands

本文探讨使用并查集算法解决二维网格地图中岛屿数量计算的问题,通过将二维坐标转换为一维索引,实现WeightedQuickUnionUF并查集算法,避免重复计算,同时对比DFS算法,提供了一种高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在看这篇文章前,你也许想要先看看并查集是如何实现的:https://blog.youkuaiyun.com/weixin_43462819/article/details/83626022

这一题是在复习完并查集之后的练手的题目。
题目是这样的:

Given a 2d grid map of '1’s (land) and '0’s (water), count the number
of islands. An island is surrounded by water and is formed by
connecting adjacent lands horizontally or vertically. You may assume
all four edges of the grid are all surrounded by water.
example:
这里是引用
Input:
11110
11010
11000
00000

Output: 1

这道题可以用多种方法来做。既然我们是为了练手并查集,那么就用并查集来做。

这里与之前我们并查集的实现中假设的输入有很大的不同:在实现中,我们是假设输入是一对一对的数;但在这里不是,这里是二维的,这就需要我们把这个二维的改造为一对一对的输入数字。这是我们所需要的。

我们选用的实现是WeightedQuickUnionUF,并且对它的构造函数进行了改动:

class WeightedQuickUnionUF {
private:
    std::vector<size_t> id;
    size_t count;
    std::vector<size_t> sz;
public: 
    WeightedQuickUnionUF(size_t m, size_t n, vector<vector<char>> &grid) {
        id.reserve(m*n);
        for (size_t i = 0; i < m*n; ++i)
            id.push_back(i);
        sz.reserve(m*n);
        for (size_t i = 0; i < m*n; ++i)
            sz.push_back(1);
        count = 0;
        for (size_t i = 0; i < m; ++i)
            for (size_t j = 0; j < n; ++j)
                if (grid[i][j] == '1')
                    ++count;
    }

    size_t Count() const {
        return count;
    }

    bool Connected(size_t p, size_t q) const {
        return Find(p) == Find(q);
    }

    size_t Find(size_t p) const {
        Validate(p);
        while (p != id[p])
            p = id[p];
        return p;
    }

    void Union(size_t p, size_t q) {
        Validate(p);
        Validate(q);

        auto pRoot = Find(p);
        auto qRoot = Find(q);

        if (pRoot == qRoot) return;

        if (sz[pRoot] < sz[qRoot]) {
            id[pRoot] = qRoot;
            sz[pRoot] += sz[qRoot];
        }
        else {
            id[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];
        }
        --count;
    }

private: 
    void Validate(size_t p) const {
        if (p >= id.size())
            throw std::out_of_range("index out of range");
    }
};

下面是另外一部分代码:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty() || grid[0].empty())
            return 0;
        auto m = grid.size();
        auto n = grid[0].size();
        WeightedQuickUnionUF uf(m, n, grid);
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == '0') continue;
                int p = i * n + j;
                /*if (i >= 1 && grid[i-1][j] == '1') {
                    int q = p - n;
                    uf.Union(p, q);
                }*/
                if (i < m-1 && grid[i+1][j] == '1') {
                    int q = p + n;
                    uf.Union(p, q);
                }
                /*if (j >= 1 && grid[i][j-1] == '1') {
                    int q = p-1;
                    uf.Union(p, q);
                }*/
                if (j < n-1 && grid[i][j+1] == '1') {
                    int q = p+1;
                    uf.Union(p, q);
                }
            }
        return uf.Count();
    }
};

值得注意的是,这里我将两种情况给注释掉了,因为这两种情况在之前的迭代里(迭代到左边一个元素和上面一个元素)的时候都已经遇到过了,所以再考虑它们就只是重复考虑了。
这样考虑,上面循环的目的就是将所有相连的情况找出来,化为union-find的输入,而不是要找到每个点的所有相邻点,所以重复的情况我们可以去掉。
我们知道之前的实现中共有三种实现,分别是quick_find, quick_union, weighted_quick_union,这里选用的实现为weighted_quick_union,它只是quick_union的一个改进,所以quick_union在这里也是可以用的。值得注意的是,quick_find能在这里使用吗?经过我的实验,也是可以的(当然也需要对它的构造函数做一些改动),并且可以和上面的代码一样删掉重复的那两种情况(只需要找出所有的相邻的数对)。但是quick_find有个问题,就是输入过大的时候会提示超时,LeetCode这种情况不给过。所以还是用quick_union或这里的weighter_quick_union比较好。

刚刚学完一种算法就能马上用上还是不错的^ ^

之前我们说了,这题目不止一种做法,还有使用DFS的做法,参考代码如下:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty() || grid[0].empty())
            return 0;
        int count = 0;
        for (int i = 0; i < grid.size(); ++i) 
            for (int j = 0; j < grid[0].size(); ++j) {
                if (grid[i][j] == '0') continue;
                dfs(grid, i, j);
                ++count;
            }
        return count;
    }
private:
    void dfs(vector<vector<char>> &grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == '0') return;
        grid[i][j] = '0';
        dfs(grid, i-1, j);
        dfs(grid, i+1, j);
        dfs(grid, i, j-1);
        dfs(grid, i, j+1);
    }
};

如果面试的时候遇到这种题目,把两种思路都说出来,应该还是能加分不少的吧。

一、综合实战—使用极轴追踪方式绘制信号灯 实战目标:利用对象捕捉追踪和极轴追踪功能创建信号灯图形 技术要点:结合两种追踪方式实现精确绘图,适用于工程制图中需要精确定位的场景 1. 切换至AutoCAD 操作步骤: 启动AutoCAD 2016软件 打开随书光盘中的素材文件 确认工作空间为"草图与注释"模式 2. 绘图设置 1)草图设置对话框 打开方式:通过"工具→绘图设置"菜单命令 功能定位:该对话框包含捕捉、追踪等核心绘图辅助功能设置 2)对象捕捉设置 关键配置: 启用对象捕捉(F3快捷键) 启用对象捕捉追踪(F11快捷键) 勾选端点、中心、圆心、象限点等常用捕捉模式 追踪原理:命令执行时悬停光标可显示追踪矢量,再次悬停可停止追踪 3)极轴追踪设置 参数设置: 启用极轴追踪功能 设置角度增量为45度 确认后退出对话框 3. 绘制信号灯 1)绘制圆形 执行命令:"绘图→圆→圆心、半径"命令 绘制过程: 使用对象捕捉追踪定位矩形中心作为圆心 输入半径值30并按Enter确认 通过象限点捕捉确保圆形位置准确 2)绘制直线 操作要点: 选择"绘图→直线"命令 捕捉矩形上边中点作为起点 捕捉圆的上象限点作为终点 按Enter结束当前直线命令 重复技巧: 按Enter可重复最近使用的直线命令 通过圆心捕捉和极轴追踪绘制放射状直线 最终形成完整的信号灯指示图案 3)完成绘制 验证要点: 检查所有直线是否准确连接圆心和象限点 确认极轴追踪的45度增量是否体现 保存绘图文件(快捷键Ctrl+S)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值