题目来源
题目描述
题目解析
深度优先遍历DFS
目标是找到矩阵中 “岛屿的数量” ,上下左右相连的 1 都被认为是连续岛屿。
dfs方法: 设目前指针指向一个岛屿中的某一点 (i, j),寻找包括此点的岛屿边界。
- 从 (i, j) 向此点的上下左右 (i+1,j),(i-1,j),(i,j+1),(i,j-1) 做深度搜索。
- 终止条件:
- (i, j) 越过矩阵边界;
- grid[i][j] == 0,代表此分支已越过岛屿边界。
- 搜索岛屿的同时,执行 grid[i][j] = ‘0’,即将岛屿所有节点删除,以免之后重复搜索相同岛屿。
- 主循环:
- 遍历整个矩阵,当遇到 grid[i][j] == ‘1’ 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿。
- 复杂度:所有的数遍历一遍,所以复杂度是O(N*M)
class Solution {
void infect(vector<vector<char>>& grid, int x, int y){
// 越界,或者不为1,那么就不需要感染
if(x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] != '1'){
return;
}
grid[x][y] = '0'; //grid[x][y] = '2'
infect(grid, x - 1, y);
infect(grid, x + 1, y);
infect(grid, x, y - 1);
infect(grid, x, y + 1);
}
public:
int numIslands(vector<vector<char>>& grid) {
if(grid.empty() || grid[0].empty()){
return 0;
}
int ans = 0, m = grid.size(), n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if(grid[i][j] == '1'){
ans++;
infect(grid, i, j);
}
}
}
return ans;
}
};
广度优先遍历 BFS
- 主循环和思路一类似,不同点是在于搜索某岛屿边界的方法不同。
- bfs 方法:
- 借助一个队列queue,判断队列首部节点(i, j)是否未越界而且为1
- 如果是则置(删除此岛屿),并将此节点上下左右节点(i+1,j),(i-1,j),(i,j+1),(i,j-1)加入队列
- 如果不是则跳过此节点
- 循环
pop
队列首节点,直到整个队列为空,此时已经遍历完此岛屿
- 借助一个队列queue,判断队列首部节点(i, j)是否未越界而且为1
class Solution {
void bfs(vector<vector<char>>& grid, int i, int j){
std::queue<pair<int, int>> list;
list.push({i, j});
while (!list.empty()){
auto curr = list.front();list.pop();
i = curr.first, j = curr.second;
if(i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == '1'){
grid[i][j] = '0';
list.push({i + 1, j});
list.push({i - 1, j});
list.push({i , j + 1});
list.push({i , j - 1});
}
}
}
public:
int numIslands(vector<vector<char>>& grid) {
if(grid.empty() || grid[0].empty()){
return 0;
}
int ans = 0, m = grid.size(), n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if(grid[i][j] == '1'){
ans++;
bfs(grid, i, j);
}
}
}
return ans;
}
};
并查集
并查集是一种树型的数据结构,用于处理一些不相交的合并以及查询问题。
步骤:
- 遍历这个二维数组,将所有的1都认为是一个单独的集合
- 遍历这个二维数组,对于每一个1,只看它的左边和上边,如果发现有1,就做union操作(为甚只看左边和上边,因为右边和下边是对称的,而我们从左到右,从上到下遍历,所以不会重复和遗落)
class Solution {
class UnionFind{
private:
std::vector<int> parent;
std::vector<int> size;
std::vector<int> help;
int cnt{};
int wid;
int index(int i, int j) const{
return i * wid + j; //x、y 表示二维坐标值,w 表示矩阵宽度。
}
int findRoot(int i){
int hi = 0;
while (parent[i] != i){
help[hi++] = i;
i = parent[i];
}
for (hi--; hi >= 0; --hi) {
parent[help[hi]] = i;
}
return i;
}
public:
explicit UnionFind(vector<vector<char>>& board){
int m = board.size(); // 矩阵行数
int n = board[0].size(); // 矩阵列数(宽度),即第一行元素数
parent.resize(m * n);
size.resize(m * n);
help.resize(m * n);
wid = n;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if(board[i][j] == '1'){
int idx = index(i, j);
parent[idx] = idx;
size[idx] = 1;
++cnt;
}
}
}
}
void merge(int i1, int j1, int i2, int j2){
int i = index(i1, j1);
int j = index(i2, j2);
int ri = findRoot(i);
int rj = findRoot(j);
if(ri != rj){
if(size[ri] >= size[rj]){
parent[rj] = ri;
size[ri] += size[rj];
}else{
parent[ri] = rj;
size[rj] += size[ri];
}
--cnt;
}
}
int count() const {
return cnt;
}
bool isConnected(int i, int j){
return findRoot(i) == findRoot(j);
}
};
public:
int numIslands(vector<vector<char>>& board) {
if(board.empty()){
return 0;
}
int m = board.size(); // 矩阵行数
int n = board[0].size(); // 矩阵列数(宽度),即第一行元素数
UnionFind unionFind(board);
// 第一列(除了(0, 0)外)
for (int i = 1; i < m; ++i) {
if(board[i - 1][0] == '1' && board[i][0] == '1'){
unionFind.merge(i - 1, 0, i, 0);
}
}
// 第一行(除了(0, 0)外)
for (int j = 1; j < n; ++j) {
if(board[0][j - 1] == '1' && board[0][j] == '1'){
unionFind.merge(0, j - 1, 0, j);
}
}
//
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if(board[i][j] == '1' ){ // 只看左&上
if(board[i - 1][j] == '1'){
unionFind.merge(i, j, i - 1, j);
}
if(board[i][j - 1] == '1'){
unionFind.merge(i, j, i, j - 1);
}
}
}
}
return unionFind.count();
}
};
类似题目
题目 | 思路 |
---|---|
leetcode:130. 被围绕的区域 Surrounded Regions | |
leetcode:695. 岛屿的最大面积Max Area of Island | |
leetcode:463. 岛屿的周长 Island Perimeter | 岛屿的周长就是岛屿方格和非岛屿方格相邻的边的数量。 |
leetcode:200. 岛屿数量 Number of Islands | dfs、并查集 |
leetcode:305. 岛屿的数量 Number of Islands II | 动态生成并查集 |
leetcode:694. 不同岛屿的数量 Number of Distinct Islands | |
leetcode:711.不同岛屿的数量 II Number of Distinct Islands II | |
leetcode:1020. 飞地的数量 number-of-enclaves | |
leetcode:1254. 统计封闭岛屿的数目 number-of-closed-islands | 只要提前把靠边的陆地都淹掉,然后算出来的就是封闭岛屿了。 |
leetcode:1905. 统计子岛屿 count-sub-islands | |
leetcode:286.给每个空房间位上填上该房间到 最近 门的距离 Walls and Gates | 从门开始扩散 |
leetcode:489. 扫地机器人 Robot Room Cleaner | 难点:怎么建立位置坐标?怎么回溯? |
leetcode:317. 离建筑物最近的距离 Shortest Distance from All Buildings | (1) 从每一个建筑物开始进行广度优先搜索 (2) 在搜索的同时计算每一个空格到这个建筑物的距离 (3) 在搜索的同时将每一个空格到每一个建筑物的距离进行累加,得到每个空格到所有建筑物的距离(4) 取空格到所有建筑物的最小距离 |
leetcode:323. 无向连通图中的连通分量个数Number of Connected Components in an Undirected Graph | 连通图 |
Rotting Oranges |