解题方法一:深度优先搜索
我们可以将二维网格看成一个无向图,竖直或水平相邻的 1 之间有边相连。
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。
最终岛屿的数量就是我们进行深度优先搜索的次数。
//深度优先搜索
class Solution {
private:
void dfs(vector<vector<char>>& grid,int r,int c){
int nr = grid.size();
int nc = grid[0].size();
grid[r][c] = '0';
if(r-1 >= 0 && grid[r-1][c]=='1') dfs(grid,r-1,c);
if(r+1 < nr && grid[r+1][c]=='1') dfs(grid,r+1,c);
if(c-1 >= 0 && grid[r][c-1]=='1') dfs(grid,r,c-1);
if(c+1 < nc && grid[r][c+1]=='1') dfs(grid,r,c+1);
}
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if(!nr) return 0;
int nc = grid[0].size();
int num_islands = 0;
for (int r=0;r<nr;r++){
for(int c=0;c<nc;c++){
if(grid[r][c] == '1'){
++num_islands;
dfs(grid,r,c);
}
}
}
return num_islands;
}
};
解题方法二:广度优先搜索
同样地,我们也可以使用广度优先搜索代替深度优先搜索。
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其加入队列,开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。直到队列为空,搜索结束。
//广度优先搜索 队列
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if(!nr) return 0;
int nc = grid[0].size();
int num_islands = 0;
for(int r=0;r<nr;++r){
for(int c=0;c<nc;++c){
if(grid[r][c]=='1'){
++num_islands;
grid[r][c]='0';
queue<pair<int,int>> neighbors;
neighbors.push({r,c});
while(!neighbors.empty()){
auto rc=neighbors.front();
neighbors.pop();
int row = rc.first,col=rc.second;
if(row-1>=0 && grid[row-1][col]=='1'){
neighbors.push({row-1,col});
grid[row-1][col] = '0';
}
if(row+1<nr && grid[row+1][col]=='1'){
neighbors.push({row+1,col});
grid[row+1][col] = '0';
}
if(col-1>=0 && grid[row][col-1]=='1'){
neighbors.push({row,col-1});
grid[row][col-1] = '0';
}
if(col+1<nc && grid[row][col+1]=='1'){
neighbors.push({row,col+1});
grid[row][col+1] = '0';
}
}
}
}
}
return num_islands;
}
};
方法一:位移
算法
如上述所说,算法由两个步骤组成:
我们通过右移,将两个数字压缩为它们的公共前缀。在迭代过程中,我们计算执行的右移操作数。
将得到的公共前缀左移相同的操作数得到结果。
//位移
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int shift = 0;
// 找到公共前缀
while (m < n) {
m >>= 1;
n >>= 1;
++shift;
}
return m << shift;
}
};
方法二:Brian Kernighan算法
//Brian Kernighan算法
class Solution{
public:
int rangeBitwiseAnd(int m,int n){
while(m<n){
n = n & (n-1);
}
return n;
}
};
Brian Kernighan算法的衍生题目:
方法一:移位实现位计数(造轮子)
//位移
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int shift = 0;
// 找到公共前缀
while (m < n) {
m >>= 1;
n >>= 1;
++shift;
}
return m << shift;
}
};
方法二:Brian Kernighan算法
还有一个位移相关的算法叫做「Brian Kernighan 算法」,它用于清除二进制串中最右边的 1。
Brian Kernighan 算法的关键在于我们每次对 number 和 number−1 之间进行按位与运算后,number 中最右边的 1 会被抹去变成 0。
//Brian Kernighan算法
class Solution{
public:
int rangeBitwiseAnd(int m,int n){
while(m<n){
n = n & (n-1);
}
return n;
}
};