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
00000Output: 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);
}
};
如果面試的時候遇到這種題目,把兩種思路都說出來,應該還是能加分不少的吧。