LeetCode:200. Number of Islands

在看这篇文章前,你也许想要先看看并查集是如何实现的:https://blog.csdn.net/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);
    }
};

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

猜你喜欢

转载自blog.csdn.net/weixin_43462819/article/details/83628052