【算法】网络图中的dfs



快乐的流畅:个人主页


个人专栏:《算法神殿》《数据结构世界》《进击的C++》

远方有一堆篝火,在为久候之人燃烧!

引言

在二维网络图中的dfs,反而一般不需要画决策树,因为在二维图像中有时候很直观可以看出决策关系,一般为上下左右搜索。

一、单词搜索


细节:

  • dfs函数设置返回值bool,以便及时调整路线
  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
  • pos设置为函数参数,方便回溯
class Solution
{
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    bool vis[7][7];
    int m, n;
public:
    bool dfs(vector<vector<char>>& board, int i, int j, string& word, int pos)
    {
        if(pos == word.size()) return true;

        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n 
            && !vis[x][y] && board[x][y] == word[pos])
            {
                if(dfs(board, x, y, word, pos + 1)) return true;
            }
        }
        vis[i][j] = false;
        return false;
    }

    bool exist(vector<vector<char>>& board, string& word)
    {
        m = board.size(), n = board[0].size();
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(board[i][j] == word[0])
                {
                    if(dfs(board, i, j, word, 1)) return true;
                }
            }
        }
        return false;
    }
};

二、黄金矿工


细节:

  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
  • sum设置为函数参数,方便回溯
class Solution
{
    int ret = 0;
    bool vis[16][16];
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;
public:
    void dfs(vector<vector<int>>& grid, int i, int j, int sum)
    {
        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && grid[x][y])
            {
                dfs(grid, x, y, sum + grid[x][y]);
            }
            else ret = max(ret, sum);
        }
        vis[i][j] = false;
    }

    int getMaximumGold(vector<vector<int>>& grid)
    {
        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])
                {
                    dfs(grid, i, j, grid[i][j]);
                }
            }
        }
        return ret;
    }
};

三、不同路径 |||


细节:

  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
  • path设置为函数参数,方便回溯
class Solution
{
    int ret = 0;
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    bool vis[20][20];
    int m, n, count = 0;
public:
    void dfs(vector<vector<int>>& grid, int i, int j, int path)
    {
        if(grid[i][j] == 2)
        {
            if(path + count == m * n) ++ret;
            return;
        }

        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && grid[x][y] != -1)
            {
                dfs(grid, x, y, path + 1);
            }
        }
        vis[i][j] = false;
    }

    int uniquePathsIII(vector<vector<int>>& grid)
    {
        m = grid.size(), n = grid[0].size();
        int si, sj;
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(grid[i][j] == -1) ++count;
                else if(grid[i][j] == 1) 
                {
                    si = i, sj = j;
                }
            }
        }

        dfs(grid, si, sj, 1);
        return ret;
    }
};

  • 本题开始往后,是floodfill算法的练习
  • floodfill算法,本质就是寻找连通块
  • 同时floodfill算法没有回溯

四、图像渲染



细节:

  • 设置向量数组dx,dy
  • 记录baseColor, newColor
class Solution
{
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int baseColor, newColor;
    int m, n;
public:
    void dfs(vector<vector<int>>& image, int i, int j)
    {
        image[i][j] = newColor;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n 
            && image[x][y] == baseColor)
            {
                dfs(image, x, y);
            }
        }
    }

    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
    {
        if(image[sr][sc] == color) return image;

        baseColor = image[sr][sc], newColor = color;
        m = image.size(), n = image[0].size();
        dfs(image, sr, sc);
        return image;
    }
};

五、岛屿数量


细节:

  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
class Solution
{
    int ret;
    bool vis[301][301];
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;
public:
    void dfs(vector<vector<char>>& grid, int i, int j)
    {
        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && grid[x][y] == '1')
            {
                dfs(grid, x, y);
            }
        }
    }

    int numIslands(vector<vector<char>>& grid)
    {
        m = grid.size(), n = grid[0].size();
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(!vis[i][j] && grid[i][j] == '1')
                {
                    dfs(grid, i, j);
                    ++ret;
                }
            }
        }
        return ret;
    }
};

六、岛屿的最大面积



细节:

  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
class Solution
{
    int ret, sum;
    vector<vector<bool>> vis;
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;
public:
    void dfs(vector<vector<int>>& grid, int i, int j)
    {
        ++sum;
        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && grid[x][y] == 1)
            {
                dfs(grid, x, y);
            }
            else ret = max(ret, sum);
        }
    }

    int maxAreaOfIsland(vector<vector<int>>& grid)
    {
        m = grid.size(), n = grid[0].size();
        vis.resize(m, vector<bool>(n));
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(!vis[i][j] && grid[i][j] == 1)
                {
                    sum = 0;
                    dfs(grid, i, j);
                }
            }
        }
        return ret;
    }
};

七、被围绕的区域


细节:

  • 正难则反,将边界上及其相连的‘O’先全部标记起来,最后遍历矩阵,将未被标记的‘O’改成‘X’
  • 设置向量数组dx,dy
  • 设置bool数组vis,实现剪枝
class Solution
{
    vector<vector<bool>> vis;
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;
public:
    void dfs(vector<vector<char>>& board, int i, int j)
    {
        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && board[x][y] == 'O')
            {
                dfs(board, x, y);
            }
        }
    }

    void solve(vector<vector<char>>& board)
    {
        m = board.size(), n = board[0].size();
        vis.resize(m, vector<bool>(n));
        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 j=0; j<n; ++j)
        {
            if(board[0][j] == 'O') dfs(board, 0, j);
            if(board[m-1][j] == 'O') dfs(board, m-1, j);
        }

        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(!vis[i][j] && board[i][j] == 'O')
                {
                    board[i][j] = 'X';
                }
            }
        }
    }
};

八、太平洋大西洋水流问题




思路:正难则反,水往高处流。

  • 利用两个标记数组,分别记录太平洋和大西洋的水可以流到的区域,找出标记数组重叠的部分即可。
  • 为了只用写一份dfs函数,将vis标记数组以参数的形式传递
  • 设置向量数组dx,dy
class Solution
{
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;
public:
    void dfs(vector<vector<int>>& heights, int i, int j, vector<vector<bool>>& vis)
    {
        vis[i][j] = true;
        for(int k=0; k<4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n
            && !vis[x][y] && heights[x][y] >= heights[i][j])//正难则反,水往高处流
            {
                dfs(heights, x, y, vis);
            }
        }
    }

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights)
    {
        m = heights.size(), n = heights[0].size();
        vector<vector<bool>> vis1(m, vector<bool>(n));
        vector<vector<bool>> vis2(m, vector<bool>(n));

        for(int i=0; i<m; ++i)
        {
            if(!vis1[i][0]) dfs(heights, i, 0, vis1);
            if(!vis2[i][n-1]) dfs(heights, i, n-1, vis2);
        }

        for(int j=0; j<n; ++j)
        {
            if(!vis1[0][j]) dfs(heights, 0, j, vis1);
            if(!vis2[m-1][j]) dfs(heights, m-1, j, vis2);
        }

        vector<vector<int>> ret;
        for(int i=0; i<m; ++i)
        {
            for(int j=0; j<n; ++j)
            {
                if(vis1[i][j] && vis2[i][j])
                {
                    ret.push_back({i, j});
                }
            }
        }
        return ret;
    }
};

九、扫雷游戏



细节:本题实际上是一道模拟题,关键是理解题意。

  • 点击位置是雷,直接改变返回
  • 不是雷,则进入dfs函数递归
  • 对于当前格子,先计算周围8个格子中雷的数量
    • 如果有雷,将当前格子改为雷的数量,返回
    • 如果无雷,将当前格子改为挖出的空方块,继续递归
  • 递归的时候,要进行8个方向的搜索,所以向量数组要存储8个方向
class Solution
{
    bool vis[51][51];
    int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1};
    int dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};
    int m, n;
public:
    void dfs(vector<vector<char>>& board, int i, int j)
    {
        if(board[i][j] == 'M') return;

        int count = 0;
        for(int k=0; k<8; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n && board[x][y] == 'M')
            {
                ++count;
            }
        }

        if(count)
        {
            board[i][j] = count + '0';
            return;
        }
        else board[i][j] = 'B';
        
        vis[i][j] = true;
        for(int k=0; k<8; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && y >= 0 && x < m && y < n && !vis[x][y])
            {
                dfs(board, x, y);
            }
        }
    }

    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click)
    {
        int ci = click[0], cj = click[1];
        if(board[ci][cj] == 'M')
        {
            board[ci][cj] = 'X';
            return board;
        }

        m = board.size(), n = board[0].size();
        dfs(board, ci, cj);
        return board;
    }
};

总结

网络图中的dfs,常用的搜索技巧,是创建向量数组dx,dy,方便简洁地用循环遍历。


真诚点赞,手有余香

  • 36
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
《图 网络与算法.pdf》是一本关于图网络与算法的PDF文件。图网络与算法是计算机科学的一个重要研究领域,该领域研究如何在图结构进行信息的处理和分析。 图是一种由节点和边构成的数据结构,节点代表实体或对象,边代表节点之间的关系。图网络与算法的主要目标是研究如何从图结构提取有用的信息,并设计高效的算法来解决与图相关的问题。 在《图 网络与算法.pdf》,可能包含了以下内容: 1. 图的基本概念:介绍了图的定义、表示方法和基本属性,如节点的度、路径、连通性等。 2. 图算法:介绍了常见的图算法,如广度优先搜索(BFS)、深度优先搜索(DFS)、最短路径算法、最小生成树算法等。这些算法可以应用于图的遍历、路径搜索、优化问题等。 3. 图网络模型:介绍了图网络的基本模型和架构,如图神经网络(Graph Neural Networks,GNN)。图神经网络是专门用于处理图数据的神经网络模型,可以学习节点或边的特征表示,进而用于节点分类、图分类、链接预测等任务。 4. 图网络应用:介绍了图网络在不同领域的应用,如社交网络分析、生物信息学、推荐系统等。这些应用展示了图网络与算法在实际问题的价值和应用前景。 总之,阅读《图 网络与算法.pdf》可以帮助读者了解图网络与算法的基本原理、常用算法和应用场景,对于从事计算机科学、数据科学以及相关领域的研究人员或学生来说,这本书是一份有价值的参考资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值