題目描述
注意,需要求的是島嶼的數量,而不是島嶼的總面積,
這道題很考驗對dfs思路的理解,而不是簡單地套用模版。
可以用dfs和bfs兩種方法做。
深度優先搜尋版本
首先看程式碼:
class Solution {
private:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四個方向
void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
for (int i = 0; i < 4; i++) {
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳過
if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { // 沒有訪問過的 同時 是陸地的
visited[nextx][nexty] = true;
dfs(grid, visited, nextx, nexty);
}
}
}
public:
int numIslands(vector<vector<char>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visited[i][j] && grid[i][j] == '1') {
visited[i][j] = true;
result++; // 遇到沒訪問過的陸地,+1
dfs(grid, visited, i, j); // 將與其連結的陸地都標記上 true
}
}
}
return result;
}
};
這個程式碼的思路是:在numIslands函式里,透過迴圈每次找到一個沒有被訪問過的島嶼。在新島嶼上找到一個起始點後,用dfs對這個島嶼上所有的陸地點標記為visited,然後再回到numIslands函式里尋找下一個新島嶼(這裡就能看出dfs操作的作用)。
但是看程式碼後會發現,這個dfs和一般的dfs程式碼很不一樣,因為在開頭沒有終止條件。理解這裡就需要對dfs或者回溯演算法的執行過程細節熟悉。
在前面學習的回溯演算法裡,需要注意,return語句並不是為了“停止遍歷”,而是為了“在合適的位置停止遍歷”,其實就算不加return語句,許多回溯函式在遍歷所有可能結果後也會自動停止並回溯,因為執行到邊界後,程式不會進入for迴圈而是直接執行到函式結尾,這個過程其實和return ;的效果是一樣的,只有在:遍歷已經到達盡頭,但是仍然會進入下一層遞迴的情況下,才有可能造成死迴圈。所以控制遞迴呼叫的位置才是避免死迴圈的關鍵。
所以在這個程式碼裡,由於dfs函式只會遍歷陸地方塊,所以不需要return語句,它自然地就會遍歷一個島嶼的所有陸地然後結束。
廣度優先搜尋版本
根據上面的演算法,廣度優先版本的思路就很容易想到,也是在numIslands裡尋找新的島嶼,在bfs函式里遍歷整個島嶼的所有陸地。
程式碼如下:
class Solution {
private:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四個方向
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true; // 只要加入佇列,立刻標記
while(!que.empty()) {
pair<int ,int> cur = que.front(); que.pop();
int curx = cur.first;
int cury = cur.second;
for (int i = 0; i < 4; i++) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳過
if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') {
que.push({nextx, nexty});
visited[nextx][nexty] = true; // 只要加入佇列立刻標記
}
}
}
}
public:
int numIslands(vector<vector<char>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visited[i][j] && grid[i][j] == '1') {
result++; // 遇到沒訪問過的陸地,+1
bfs(grid, visited, i, j); // 將與其連結的陸地都標記上 true
}
}
}
return result;
}
};
可以看出,bfs中沒有用到遞迴,而是透過迴圈實現的。
透過que.push({nextx, nexty});語句,每次將後續結點入隊,直到que.empty(),即沒有任何後繼結點,也就是所有結點都被遍歷過,這時結束迴圈。
一種可能出現的錯誤: