LeetCode——图

200. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。
在这里插入图片描述

方法一:深度优先搜索

  • 将二维网格看成一个无向图,竖直或水平相邻的1之间有边相连。
  • 扫描整个二维网格,遍历每个元素。
  • 如果该位置为1,就将它作为起始节点开始深度优先搜索,并将其置为0,然后考察该节点的上下左右结点,看是否属于岛屿部分
  • 经过上一步骤的深度优先搜索,将属于同一岛屿内的元素都置为了0,遍历网格再遇到的1属于新的岛屿,岛屿数加一
  • 最终的岛屿数就是进行深度优先搜索的次数
    在这里插入图片描述
    代码
class Solution {
private:
    void dfs(vector<vector<char>>& grid, int r, int c) {
        //深度优先搜索
        int nr = grid.size();   //获得数组的行数
        int nc = grid[0].size();//列数

        //将当前格的值设为0,表示已经遍历过了
        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;
        //遍历每个数组元素,如果值为1,就调用dfs函数进行深度优先搜索
        //深度优先搜索的过程中会将属于同一岛屿(上下左右相邻的1)的元素置为0,即下次再遇到1时说明遇到了新的岛屿,岛屿数加一
        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;
                        //考察当前结点的上下左右是否为1,为1就加入队列,然后将其置为0,代表已经遍历过了
                        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;
    }
};

733. 图像渲染(遍历邻接点,队列)

一个图像(二维数组),每个元素的数值在065535之间,给定坐标(sr,sc),表示图像渲染开始的位置,以及一个新的颜色值newColor,如果该位置原本的值就等于新颜色值,那就不进行渲染,直接返回image
渲染从初始坐标开始,考察它的上下左右四个方向的值,如果等于初始坐标的值,就对他进行渲染,然后再看它的邻接点,返回经过上色渲染后的图像
在这里插入图片描述

思路

利用广度优先遍历

  • 用一个辅助队列来保存没有访问过并且值等于初始坐标值的点
  • 官方题解对于访问邻接点有个很好的方法,先定义两个数组
const int dx[4] = {1, 0, 0, -1};
const int dy[4] = {0, 1, -1, 0};
  • 然后只需要在中心坐标的基础上加数组元素,就能实现上下左右四个邻接点的遍历
for (int i = 0; i < 4; i++) {
                int mx = x + dx[i], my = y + dy[i];
                if (mx >= 0 && mx < n && my >= 0 && my < m && image[mx][my] == currColor) {
                    que.emplace(mx, my);
                    image[mx][my] = newColor;
                }
            }

代码

class Solution {
public:
    //用于访问邻接点
    const int dx[4] = {1, 0, 0, -1};
    const int dy[4] = {0, 1, -1, 0};
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        int currColor = image[sr][sc];
        if (currColor == newColor) return image;
        int n = image.size(), m = image[0].size();
        //辅助队列
        queue<pair<int, int>> que;
        //初始点入队
        que.emplace(sr, sc);
        //渲染初始点
        image[sr][sc] = newColor;
        while (!que.empty()) {
            int x = que.front().first, y = que.front().second;
            que.pop();
            for (int i = 0; i < 4; i++) {
            	//考察当前位置的上下左右坐标
                int mx = x + dx[i], my = y + dy[i];
                //满足条件的结点入队
                if (mx >= 0 && mx < n && my >= 0 && my < m && image[mx][my] == currColor) {
                    que.emplace(mx, my);
                    image[mx][my] = newColor;
                }
            }
        }
        return image;
    }
};

深度优先遍历
一条路走到黑
代码

class Solution {
public:
    //上下左右坐标
    int x[4]={1,0,0,-1};
    int y[4]={0,1,-1,0};
    //深度优先遍历
    void DFS(vector<vector<int>>& image,int sr, int sc, int newColor,int old,int r,int c)
    {
        image[sr][sc]=newColor;
        for(int i=0;i<4;i++)
        {
            int m_x=sr+x[i];
            int m_y=sc+y[i];
            if(m_x<r&&m_x>=0&&m_y<c&&m_y>=0)
            {
                if(image[m_x][m_y]==old)
                {
                    DFS(image,m_x,m_y,newColor,old,r,c);
                }
            }
        }
    }
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        //深度优先,一条路走到黑
        if(image[sr][sc]==newColor)
        return image;
        int m=image.size();
        int n=image[0].size();
        int oldcolor=image[sr][sc];

        //启动深度优先遍历
        DFS(image,sr,sc,newColor,oldcolor,m,n);

        return image;
    }
};

542. 01 矩阵(队列)

给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1
示例

思路

  • 参照官方题解
  • 题目要求是对于每个1,找到离他最近的0
  • 题目中可能存在多个0,为了找到离1最近的那个0,可以从0的位置开始进行广度优先搜索
  • 广度优先搜索可以找到从起点到其余所有点的 最短距离,因此如果我们从 0 开始搜索,每次搜索到一个 1,就可以得到 0 到这个 11 的最短距离
  • 对于多个0,将这些0全部加入队列中,并且作为已访问过的点
    算法

代码

class Solution {
public:
    int dx[4]={1,0,0,-1};
    int dy[4]={0,1,-1,0};
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        //对矩阵中的每个1开启广度优先搜索,一层一层地向外扩张找0
        //开启的搜索次数是该1到最近的0的距离
        int r=mat.size();
        int c=mat[0].size();
        
        //结果矩阵
        vector<vector<int>>res(r,vector<int>(c,0));
        //已访问的结点
        vector<vector<int>>V(r,vector<int>(c,0));
        //辅助队列
        queue<pair<int,int>>Q;
        //遍历原矩阵的每个元素,将0加入队列
        for(int i=0;i<r;i++)
        {
            for(int j=0;j<c;j++)
            {
                if(mat[i][j]==0)
                {
                    Q.emplace(i,j);
                    V[i][j]=1;
                }
            }
        }
        
        while(!Q.empty())
        {
            int x=Q.front().first;
            int y=Q.front().second;
            Q.pop();
            for(int i=0;i<4;i++)
            {
                int mx=x+dx[i];
                int my=y+dy[i];
                //遇到没访问过得非0点
                if(mx>=0 && mx<r && my>=0 && my<c && !V[mx][my])
                {
                    res[mx][my]=res[x][y]+1;
                    Q.emplace(mx,my);
                    V[mx][my]=1;
                }
            }
        }
        return res;
    }
    //广度优先搜索,找0
    int BFS(vector<vector<int>>& mat,vector<vector<int>>& V,int nr,int nc)
    {
        int r=mat.size();
        int c=mat[0].size();
        //辅助队列
        queue<pair<int,int>>Q;
        Q.emplace(nr,nc);
        V[nr][nc]=1;
        int n=1;
        while(!Q.empty())
        {
            int x=Q.front().first;
            int y=Q.front().second;
            Q.pop();
            for(int i=0;i<4;i++)
            {
                int mx=x+dx[i];
                int my=y+dy[i];
                if(mx>=0 && mx<r && my>=0 && my<c && !V[mx][my])
                {
                    if(mat[mx][my]==1)
                    {
                        Q.emplace(mx,my);
                        V[mx][my]=1;
                    }
                    else if(mat[mx][my]==0)
                    return n;
                }
            }
            n++;
        }
        return 0;
    }
};

994. 腐烂的橘子

在给定的网格中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。

每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
示例
思路

  • 类似上一道01矩阵,利用多源广度优先搜索来解决问题,先将所有腐烂橘子加入队列,如果一开始就没有腐烂橘子,返回0
  • 每次出队时,看它周围有没有新鲜橘子(1),有的话,将它也腐蚀掉,置为2,并将这个橘子也加入队列
  • 每一轮中出队的橘子是属于同一层的,每一轮结束后计数器会加1
  • 一轮一轮地向外腐蚀扩张,直到再也找不到新鲜橘子可以加入队列,跳出循环
  • 跳出后,检查是否还存在没有被腐蚀的新鲜橘子,如果有的话,返回-1
  • 计数的时候有点小问题,我设置的是每一轮出队结束后+1,但是最后一轮出队时,腐烂橘子周围已经没有新鲜橘子可以腐蚀了,没有腐蚀的过程,多加了一个1,要减去

代码

class Solution {
public:
    int dx[4]={1,0,0,-1};
    int dy[4]={0,1,-1,0};
    int orangesRotting(vector<vector<int>>& grid) {
        //类似01矩阵那道题
        int m=grid.size();
        int n=grid[0].size();
        int ans=0;
        queue<pair<int,int>>Q;
        //将腐烂橘子加入队列
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==2)
                Q.emplace(i,j);
            }
        }
        while(!Q.empty())
        {
            //每次出队的是同一轮中被腐蚀的橘子
            int size=Q.size();
            for(int k=0;k<size;k++)
            {
                int mx=Q.front().first;
                int my=Q.front().second;
                Q.pop();
                for(int i=0;i<4;i++)
                {
                    int x=mx+dx[i];
                    int y=my+dy[i];
                    if(x>=0 && x<m && y>=0 && y<n && grid[x][y]==1)
                    {
                        Q.emplace(x,y);
                        grid[x][y]=2;
                    }
                }
            }
            ans++;
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1)
                return -1;
            }
        }
        //最后一轮出队时,没有找到新鲜橘子,次数上多加了一次
        if(ans)
        return ans-1;

        return ans;
    }
};

复杂度

  • 时间O(m×n)
  • 空间O(m×n),队列中存放的元素最多m×n

695. 岛屿的最大面积

给定一个包含了一些 01 的非空二维数组 grid
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)
示例
思路:

  • 遍历图像每个元素,遇到1就启动广度优先搜索,看他周围有多少1
  • 广度优先遍历中,将当前元素加入队列,计数+1
  • 然后看他上下左右四个数,如果是1的话,就再加入队列
  • 队列中非空时,出队,然后继续向周围扩散
  • 访问过的元素都要标记为0

代码

class Solution {
public:
    int x[4]={1,0,0,-1};
    int y[4]={0,1,-1,0};

    //广度优先
    int BFS(vector<vector<int>>& grid,int sr,int sc,int s,int c)
    {
        int n=1;
        //辅助队列
        queue<pair<int,int>>Q;
        Q.emplace(sr,sc);
        grid[sr][sc]=0;
        while(!Q.empty())
        {
            int i=Q.front().first;
            int j=Q.front().second;
            Q.pop();
            for(int k=0;k<4;k++)
            {
                int m_x=i+x[k];
                int m_y=j+y[k];
                if(m_x>=0&&m_x<s&&m_y>=0&&m_y<c)
                {
                    if(grid[m_x][m_y])
                    {
                        n++;
                        Q.emplace(m_x,m_y);
                        grid[m_x][m_y]=0;
                    }
                }
            }
        }
        return n;
    }
    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++)
            {
                //遇到岛屿,开启广度优先搜索,看这个岛屿有多少1
                if(grid[i][j]==1)
                {
                    int tmp=BFS(grid,i,j,m,n);
                    ans=max(ans,tmp);
                }
            }
        }
        return ans;
    }
};

复杂度

  • 时间O(n*m)n是网格行数,m是网格列数,每个网格只返问一次
  • 空间O(n*m),队列中最多会存放所有的土地
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值