岛屿类问题
问题基础
如何在二维矩阵中使用 DFS 搜索呢?如果你把二维矩阵中的每一个位置看做一个节点,这个节点的上下左右四个位置就是相邻节点,那么整个矩阵就可以抽象成一幅网状的「图」结构。
根据二叉树的遍历框架改写出二维矩阵的 DFS 代码框架:
// 二叉树遍历框架
void traverse(TreeNode root) {
traverse(root.left);
traverse(root.right);
}
// 二维矩阵遍历框架
void dfs(int[][] grid, int i, int j, boolean[][] visited) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (visited[i][j]) {
// 已遍历过 (i, j)
return;
}
// 进入节点 (i, j)
visited[i][j] = true;
dfs(grid, i - 1, j, visited); // 上
dfs(grid, i + 1, j, visited); // 下
dfs(grid, i, j - 1, visited); // 左
dfs(grid, i, j + 1, visited); // 右
visited[i][j] = false;
}
DFS
200. 岛屿数量
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
int n = grid[0].size();
int ans = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]=='1'){
dfs(grid,i,j);
ans++;
}
}
}
return ans;
}
void dfs(vector<vector<char>>&grid, int i,int j){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n){
return;
}
if(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);
return;
}
};
694. 不同的岛屿数量
class Soultion694
{
public:
Soultion694() {}
public:
int numDistinctIslands(vector<vector<int>>& grid){
int m = grid.size();
int n = grid[0].size();
unordered_map<string,int> hashmap;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
string res;
dfs(grid,i,j,res,666);
hashmap[res]++;
}
}
}
return hashmap.size();
}
void dfs(vector<vector<int>>& grid, int i, int j, string& str, int dir){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n){
return;
}
if(grid[i][j]==0){
return;
}
grid[i][j]=0;
str += std::to_string(dir)+",";
dfs(grid,i-1,j,str,1);
dfs(grid,i+1,j,str,2);
dfs(grid,i,j-1,str,3);
dfs(grid,i,j+1,str,4);
str += std::to_string(-dir)+",";
return;
}
};
695. 岛屿的最大面积
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int ans = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
int area = 0;
dfs(grid,i,j,area);
ans = max(ans,area);
}
}
}
return ans;
}
void dfs(vector<vector<int>>& grid, int i,int j,int& area){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n)
return;
if(grid[i][j]==0)
return;
area++;
grid[i][j]=0;
dfs(grid,i-1,j,area);
dfs(grid,i+1,j,area);
dfs(grid,i,j-1,area);
dfs(grid,i,j+1,area);
return;
}
};
711. Number of Distinct Islands II
class Solution711
{
public:
Solution711() {}
int numDistinctIslands2(vector<vector<int>>& grid){
int m = grid.size();
int n = grid[0].size();
set<vector<pair<int,int>>> distinct;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
vector<pair<int,int>> pattern;
dfs(grid,pattern,i,j,i,j);
distinct.insert(normalize(pattern));
}
}
}
return distinct.size();
}
void dfs(vector<vector<int>>&grid, vector<pair<int,int>>& pattern, int i0, int j0, int i, int j){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n){
return;
}
if(grid[i][j]<=0){
return;
}
grid[i][j]=-1;
pattern.emplace_back(i-i0,j-j0);
static constexpr int dx[4] = {-1,1,0,0};
static constexpr int dy[4] = {0,0,-1,1};
for(int k=0;k<4;k++){
dfs(grid,pattern ,i0,j0,i+dx[k],j+dy[k]);
}
return;
}
vector<pair<int,int>> normalize(vector<pair<int,int>> pattern){
vector<vector<pair<int,int>>> ret(8,vector<pair<int,int>>());
for(auto cur:pattern){
int x = cur.first, y = cur.second;
ret[0].emplace_back(x,y);
ret[1].emplace_back(-x,y);
ret[2].emplace_back(x,-y);
ret[3].emplace_back(-x,-y);
ret[4].emplace_back(y,x);
ret[5].emplace_back(y,-x);
ret[6].emplace_back(-y,x);
ret[7].emplace_back(-y,-x);
}
for(int i = 0;i < 8; i++){
sort(ret[i].begin(),ret[i].end()); // 对八种形状按照从上到下和从左到右进行排序 1的位置
int r_offset = 0 - ret[i][0].first; // 将每种形状的最上边 最左边的1的位置记录为(0,0)
int l_offset = 0 - ret[i][0].second; // 统一规规范化 最其中最小的
for(int j=0;j<ret[i].size();j++){
ret[i][j].first += r_offset;
ret[i][j].second += l_offset;
}
}
sort(ret.begin(),ret.end());
return ret[0];
}
};
1020. 飞地的数量
class Solution {
public:
int numEnclaves(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
for(int i=0;i<m;i++){
if(grid[i][0]==1){
dfs(grid,i,0);
}
if(grid[i][n-1]==1){
dfs(grid,i,n-1);
}
}
for(int i=0;i<n;i++){
if(grid[0][i]==1){
dfs(grid,0,i);
}
if(grid[m-1][i]==1){
dfs(grid,m-1,i);
}
}
int count{0};
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(grid[i][j]==1){
count++;
}
}
}
return count;
}
void dfs(vector<vector<int>>&grid,int i,int j){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n)
return;
if(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);
}
};
1254. 统计封闭岛屿的数目
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
for(int i=0;i<m;i++){
if(grid[i][0]==0){
dfs(grid,i,0);
}
if(grid[i][n-1]==0){
dfs(grid,i,n-1);
}
}
for(int i=0;i<n;i++){
if(grid[0][i]==0){
dfs(grid,0,i);
}
if(grid[m-1][i]==0){
dfs(grid,m-1,i);
}
}
int count{0};
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(grid[i][j]==0){
dfs(grid,i,j);
count++;
}
}
}
return count;
}
void dfs(vector<vector<int>>&grid, int i,int j){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n)
return;
if(grid[i][j]==1)
return;
grid[i][j]=1;
dfs(grid,i-1,j);
dfs(grid,i+1,j);
dfs(grid,i,j-1);
dfs(grid,i,j+1);
return;
}
};
1905. 统计子岛屿
class Solution {
public:
int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
int m = grid1.size();
int n = grid1[0].size();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid1[i][j]==0&&grid2[i][j]==1){
dfs(grid2,i,j);
}
}
}
int count{0};
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid2[i][j]==1){
dfs(grid2,i,j);
count++;
}
}
}
return count;
}
void dfs(vector<vector<int>>& grid2,int i,int j){
int m = grid2.size();
int n = grid2[0].size();
if(i<0||i>=m||j<0||j>=n)
return;
if(grid2[i][j]==0)
return;
grid2[i][j]=0;
dfs(grid2,i-1,j);
dfs(grid2,i+1,j);
dfs(grid2,i,j-1);
dfs(grid2,i,j+1);
return;
}
};
463. 岛屿的周长
这道题目主要需要清楚,1格子的每条边被算作周长当且仅当这条边为网格边界或者相邻的另一个格子为水域。因此,可以遍历每个陆地格子,对其四个方向是否为边界或者水域,通过遍历就可以得到。
class Solution {
public:
int islandPerimeter(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int ans = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
dfs(grid,i,j,ans);
}
}
}
return ans;
}
void dfs(vector<vector<int>>&grid, int i,int j,int& ans){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n){
ans++;
return;
}
if(grid[i][j]==0){
ans++;
return;
}
if(grid[i][j]==2)
return;
grid[i][j]=2;
dfs(grid,i-1,j,ans);
dfs(grid,i+1,j,ans);
dfs(grid,i,j-1,ans);
dfs(grid,i,j+1,ans);
return;
}
};
剑指 Offer II 105. 岛屿的最大面积
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int ans = 0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
int area{0};
dfs(grid,i,j,area);
ans = max(area, ans);
}
}
}
return ans;
}
void dfs(vector<vector<int>>& grid, int i,int j,int& area){
int m = grid.size();
int n = grid[0].size();
if(i<0||i>=m||j<0||j>=n){
return;
}
if(grid[i][j]==0)
return;
grid[i][j]=0;
area++;
dfs(grid,i-1,j,area);
dfs(grid,i+1,j,area);
dfs(grid,i,j-1,area);
dfs(grid,i,j+1,area);
return;
}
};
130. 被围绕的区域
class Solution {
public:
void solve(vector<vector<char>>& board) {
int m = board.size();
int n = board[0].size();
for(int i=0;i<m;i++){
if(board[i][0]=='O')
dfs(board,i,0,'#');
if(board[i][n-1]=='O')
dfs(board,i,n-1,'#');
}
for(int i=0;i<n;i++){
if(board[0][i]=='O')
dfs(board,0,i,'#');
if(board[m-1][i]=='O')
dfs(board,m-1,i,'#');
}
// for(int i=0;i<m;i++){
// for(int j=0;j<n;j++){
// if(board[i][j]=='O'){
// dfs(board,i,j,'X');
// }
// }
// }
// for(int i=0;i<m;i++){
// for(int j=0;j<n;j++){
// if(board[i][j]=='#'){
// dfs(board,i,j,'O');
// }
// }
// }
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(board[i][j]=='#'){
board[i][j]='O';
}
else if (board[i][j]=='O') {
board[i][j]='X';
}
}
}
}
void dfs(vector<vector<char>>&board, int i,int j, char ch){
int m = board.size();
int n = board[0].size();
if(i<0||i>=m||j<0||j>=n)
return;
if(board[i][j]==ch || board[i][j]=='X')
return;
board[i][j]=ch;
dfs(board,i-1,j,ch);
dfs(board,i+1,j,ch);
dfs(board,i,j-1,ch);
dfs(board,i,j+1,ch);
return;
}
};
827. 最大人工岛
class Solution {
public:
inline bool inArea(const vector<vector<int>>& grid,int i,int j){
return i>=0 && i<grid.size() && j>=0 && j<grid[0].size();
}
int dfs(vector<vector<int>>& grid, int i, int j, int index){
if(!inArea(grid,i,j))
return 0;
if(grid[i][j]!=1)
return 0;
grid[i][j]=index;
return (1+
dfs(grid,i-1,j,index)+
dfs(grid,i+1,j,index)+
dfs(grid,i,j-1,index)+
dfs(grid,i,j+1,index));
}
set<int> findNeighbour(const vector<vector<int>>& grid, int i, int j){
set<int> hashset;
if(inArea(grid,i-1,j)&&grid[i-1][j]!=0)
hashset.insert(grid[i-1][j]);
if(inArea(grid,i+1,j)&&grid[i+1][j]!=0)
hashset.insert(grid[i+1][j]);
if(inArea(grid,i,j-1)&&grid[i][j-1]!=0)
hashset.insert(grid[i][j-1]);
if(inArea(grid,i,j+1)&&grid[i][j+1]!=0)
hashset.insert(grid[i][j+1]);
return hashset;
}
int largestIsland(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int index = 2;
int maxArea = 0;
if(m==0)
return 1;
unordered_map<int,int> hashmap;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
int area = 0;
area = dfs(grid,i,j,index);
hashmap[index] = area;
index++;
maxArea = max(maxArea, area);
}
}
}
if(maxArea==0)
return 1;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==0){
set<int> hashset = findNeighbour(grid,i,j);
if(hashset.empty())
continue;
int area = 1;
for(auto it=hashset.begin();it!=hashset.end();it++){
area += hashmap[*it];
}
maxArea = max(maxArea, area);
}
}
}
return maxArea;
}
};
UF
305. 岛屿数量 II
class Solution305
{
public:
class UF
{
public:
int amounts;
vector<int> parent;
vector<int> size;
public:
UF() {}
UF(int n){
amounts = n;
parent.resize(n);
size.resize(n);
for(int i=0;i<n;i++){
parent[i]=i;
size[i]=1;
}
}
int find(int index){
while (parent[index]!=index) {
parent[index] = parent[parent[index]];
index = parent[index];
}
return parent[index];
}
bool isconnected(int p,int q){
int rootP = find(p);
int rootQ = find(q);
return rootP==rootQ;
}
int count(){
return amounts;
}
void uion(int p,int q){
int rootP = find(p);
int rootQ = find(q);
if(rootP==rootQ)
return;
if(size[rootP]>size[rootQ]){
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
}
else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
amounts--;
return;
}
};
public:
Solution305() {}
vector<int> numIslands2(int m, int n,const vector<vector<int>>& positions){
vector<int> parents(m*n,-1);
vector<vector<int>> grid(m,vector<int>(n,0));
vector<int> ans(positions.size(),0);
static constexpr int dx[4] = {0,0,-1,1};
static constexpr int dy[4] = {-1,1,0,0};
int count = 0;
for(int i=0;i<positions.size();i++){
int cx = positions[i][0], cy = positions[i][1];
int id = cx*n+cy;
if(parents[id]!=-1){ // 重复添加
ans[i] = count;
continue;
}
grid[cx][cy]=1; // 新添加
parents[id]=id;
count++;
for(int k = 0;k<4;k++){
int nx = cx + dx[k];
int ny = cy + dy[k];
int nxt_id = nx * n + ny;
if(nx<0||nx>=m||ny<0||ny>=n||grid[nx][ny]==0||parents[nxt_id]==-1) // 单独的岛屿的父节点是自己
continue;
else {
int rootid = findRoot(parents,id);
int rootnxt_id = findRoot(parents,nxt_id);
if(rootid!=rootnxt_id){ // 发现相邻岛屿 且和自身的父节点不同 可以进行合并 那么就不是新岛屿
parents[rootid]==rootnxt_id;
count--;
}
}
}
ans[i] = count;
}
return ans;
}
int findRoot(vector<int>& parents, int index){
while (parents[index]!=index) {
parents[index] = parents[parents[index]];
index = parents[index];
}
return index;
}
};