面试热点题:最大人工岛 一个没有那么难的的困难题DFS

如果你一点也不了解什么是DFS(深度优先搜索),建议看一下这一篇LeetCode岛屿问题DFS


827.最大人工岛

给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。
返回执行此操作后,grid 中最大的岛屿面积是多少?
岛屿 由一组上、下、左、右四个方向相连的 1 形成。
来源:力扣(LeetCode)
在这里插入图片描述
我们以下图来分析:
在这里插入图片描述

思路

我们不难想到,这一题主要就是改变一个海洋’0’变成陆地’1’,我们可以计算每个陆地的大小,然后在其周围寻找是否有相邻的陆地,相加再判断其大小是否是最大的,这样一个一个暴力遍历,就可以AC了。

我们首先要写一个搜寻该陆地的大小:

int max_size(vector<vector<int>>& grid,int i,int j,int n)
{
    if(i<0 || i>=grid.size() || j<0 || j>=grid.size() || grid[i][j]!=1)
    {
        return 0;
    }
    grid[i][j]=n;
    return 1 + max_size(grid,i+1,j,n)+
        max_size(grid,i-1,j,n)+
        max_size(grid,i,j+1,n)+
        max_size(grid,i,j-1,n);
}

这里我们将已经遍历过的陆地更改为数字也是有讲究的:
假如一开始是这样的:
在这里插入图片描述
如果我们直接以它们的最大面积的大小来代替它们的陆地编号比如:
在这里插入图片描述
但是下面情况我们不好去处理:
在这里插入图片描述
所以,我们可以将更改的陆地数字作为下标,面积大小存储与数组中,与下标对应,这样我们就能很好处理上述,一块陆地计算两次情况。
在这里插入图片描述

处理边界

接下来我们就

  • 要么继续遍历陆地格子,然后在其判断边界是否为海洋格子的时候进行处理
  • 要么我们直接遍历海洋格子,然后在海洋格子的四周搜寻陆地

我的建议是遍历海洋格子,因为要继续遍历陆地格子,那么我们又要进DFS递归,这种递归的边界判断很难把握。

int judge(vector<vector<int>>& grid,vector<int>& arr,int i,int j)
{
    int sum=0;
    vector<int> _arr;
    if(tof(grid,i-1,j) && find(_arr.begin(),_arr.end(),grid[i-1][j])==_arr.end() && grid[i-1][j]!=0)
    {
        sum+=arr[grid[i-1][j]-2];
        _arr.push_back(grid[i-1][j]);
    }
    if(tof(grid,i+1,j) && find(_arr.begin(),_arr.end(),grid[i+1][j])==_arr.end() && grid[i+1][j]!=0)
    {
        sum+=arr[grid[i+1][j]-2];
        _arr.push_back(grid[i+1][j]);
    }
    if(tof(grid,i,j-1) && find(_arr.begin(),_arr.end(),grid[i][j-1])==_arr.end() && grid[i][j-1]!=0)
    {
        sum+=arr[grid[i][j-1]-2];
        _arr.push_back(grid[i][j-1]);
    }
    if(tof(grid,i,j+1) && find(_arr.begin(),_arr.end(),grid[i][j+1])==_arr.end() && grid[i][j+1]!=0)
    {
        sum+=arr[grid[i][j+1]-2];
        _arr.push_back(grid[i][j+1]);
    }
    return sum;
}

我的思路就是直接暴力搜索上下左右,然后将已加过的陆地存入临时数组来判断,避免增加重复陆地的情况。

最终代码:

class Solution {
public:
	//DFS返回面积
    int max_size(vector<vector<int>>& grid,int i,int j,int n)
    {
        if(i<0 || i>=grid.size() || j<0 || j>=grid.size() || grid[i][j]!=1)
        {
            return 0;
        }
        grid[i][j]=n;
        return 1 + max_size(grid,i+1,j,n)+
            max_size(grid,i-1,j,n)+
            max_size(grid,i,j+1,n)+
            max_size(grid,i,j-1,n);
    }
	//判断海洋四周面积总和
    int judge(vector<vector<int>>& grid,vector<int>& arr,int i,int j)
    {
        int sum=0;
        vector<int> _arr;
        if(tof(grid,i-1,j) && find(_arr.begin(),_arr.end(),grid[i-1][j])==_arr.end() && grid[i-1][j]!=0)
        {
            sum+=arr[grid[i-1][j]-2];
            _arr.push_back(grid[i-1][j]);
        }
        if(tof(grid,i+1,j) && find(_arr.begin(),_arr.end(),grid[i+1][j])==_arr.end() && grid[i+1][j]!=0)
        {
            sum+=arr[grid[i+1][j]-2];
            _arr.push_back(grid[i+1][j]);
        }
        if(tof(grid,i,j-1) && find(_arr.begin(),_arr.end(),grid[i][j-1])==_arr.end() && grid[i][j-1]!=0)
        {
            sum+=arr[grid[i][j-1]-2];
            _arr.push_back(grid[i][j-1]);
        }
        if(tof(grid,i,j+1) && find(_arr.begin(),_arr.end(),grid[i][j+1])==_arr.end() && grid[i][j+1]!=0)
        {
            sum+=arr[grid[i][j+1]-2];
            _arr.push_back(grid[i][j+1]);
        }
        return sum;
    }
    bool tof(vector<vector<int>>& grid,int i,int j)
    {
        if(i<0 || i>=grid.size() || j<0 || j>=grid.size())
        {
            return false;
        }
        return true;
    }
    int largestIsland(vector<vector<int>>& grid) {
        vector<int> arr;
        int x=2;
        //第一次循环是用来找寻各个陆地面积,以及将面积大小存入以其陆地数字为下标的数组
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid.size();j++)
            {
                if(grid[i][j]==1)
                {
                    int sum=max_size(grid,i,j,x);
                    x++;
                    arr.push_back(sum);
                }
            }
        }
        //第二次循环是用来找寻最大面积
        int max=0;
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid.size();j++)
            {
                if(grid[i][j]==0)
                {
                    int sum = judge(grid,arr,i,j);
                    if(sum>max)
                    {
                        max=sum;
                    }
                }
            }
        }
        if(arr.empty())
        {
            return 1;
        }
        if(max==0)
        {
            return grid.size()*grid.size();
        }
        return max<(grid.size()*grid.size())?max+1:max;
    }
};
  • 最后的判断,如果grid没有陆地,我们自己会填入一个陆地,所以要返回1。
  • max==0 是有可能没有海洋格子,循环没有进去,所以max=0,实则是满陆地格子
  • max<(grid.size()*grid.size())?max+1:max 这个要判断是因为我写的时候没有加入填的陆地,所以要判断一下。

优化思路

每次深搜遍历计算最大岛屿面积,做了很多重复的工作,只要用一次深搜把每个岛屿的面积记录下来就好。

  1. 一次遍历地图,得出各个岛屿的面积,并做编号记录。可以使用map记录,key为岛屿编号,value为岛屿面积
  2. 在遍历地图,遍历0的方格(因为要将0变成1),并统计该1(由0变成的1)周边岛屿面积,将其相邻面积相加在一起,遍历所有 0 之后,就可以得出 选一个0变成1 之后的最大面积。
class Solution {
private:
    int count;
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
    void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y, int mark) {
        if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水
        visited[x][y] = true; // 标记访问过
        grid[x][y] = mark; // 给陆地标记新标签
        count++;
        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;  // 越界了,直接跳过
            dfs(grid, visited, nextx, nexty, mark);
        }
    }

public:
    int largestIsland(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false)); // 标记访问过的点
        unordered_map<int ,int> gridNum;
        int mark = 2; // 记录每个岛屿的编号
        bool isAllGrid = true; // 标记是否整个地图都是陆地
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) isAllGrid = false;
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 0;
                    dfs(grid, visited, i, j, mark); // 将与其链接的陆地都标记上 true
                    gridNum[mark] = count; // 记录每一个岛屿的面积
                    mark++; // 记录下一个岛屿编号
                }
            }
        }
        if (isAllGrid) return n * m; // 如果都是陆地,返回全面积

        // 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
        int result = 0; // 记录最后结果
        unordered_set<int> visitedGrid; // 标记访问过的岛屿
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int count = 1; // 记录连接之后的岛屿数量
                visitedGrid.clear(); // 每次使用时,清空
                if (grid[i][j] == 0) {
                    for (int k = 0; k < 4; k++) {
                        int neari = i + dir[k][1]; // 计算相邻坐标
                        int nearj = j + dir[k][0];
                        if (neari < 0 || neari >= grid.size() || nearj < 0 || nearj >= grid[0].size()) continue;
                        if (visitedGrid.count(grid[neari][nearj])) continue; // 添加过的岛屿不要重复添加
                        // 把相邻四面的岛屿数量加起来
                        count += gridNum[grid[neari][nearj]];
                        visitedGrid.insert(grid[neari][nearj]); // 标记该岛屿已经添加过
                    }
                }
                result = max(result, count);
            }
        }
        return result;
    }
};


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

侠客cheems

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值