深度优先搜索(Depth First Search)


遍历每一个点都是一条道走到黑才停止下来,所以也被称为深度优先搜索

1. LeetCode690题—员工的重要性

链接: https://leetcode-cn.com/problems/employee-importance/
在这里插入图片描述
题解:这道题的关键在于问题的最后说的是“这个员工和他所有下属的重要度之和”,当拿到第一个直系下属的id的时候,加上他的重要度,然后再去求直系下属的下属,所以这里是有点像二叉树的前序遍历的,先要一条道走到无路可走的时候,在考虑返回的问题

class Solution {
public:
    void dfs(unordered_map<int,Employee*>& idmap,int& sum,int id)
    {
        sum += idmap[id]->importance;
        for(auto& subid: idmap[id]->subordinates)
        {
            dfs(idmap,sum,subid);
        }
    }
    //从题目可以看到,她最后问的是返回所有下属的重要度之和
    int getImportance(vector<Employee*> employees, int id) {
        //我希望能够通过id可以直接的找到这个员工的所有信息
        unordered_map<int,Employee*> Idmap;
        for(auto& e : employees)
        {
            Idmap[e->id] = e;
        }
        //上面就很好的把id和对应的员工信息进行了绑定
        int sum = 0;
        dfs(Idmap,sum,id);
        return sum;
    }
};

2. LeetCode733题—图像渲染

链接: https://leetcode-cn.com/problems/flood-fill/
在这里插入图片描述
题解:
对于这道题需要一个辅助数组,这个数组用来记录每个点是否有被访问过。

  1. 第一步就是修改题目中所要渲染的点
  2. 然后以这个点为中心去向他的前后左右4个方向寻找合法的点(判断边界)
  3. 如果找到了,那就判断是否和原来颜色相同,且没有被访问过
  4. 以该点为中心在重复上面的步骤
int nextposition[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
class Solution {
public:
    void DFS(vector<vector<int>>& image,vector<vector<int>>& visited,int row,int col,int sr,int sc,int oldColor,int newColor)
    {
        //第一步就是修改我们所给的坐标
        image[sr][sc] = newColor;
        visited[sr][sc] = 1; //表示这个位置被遍历过了
        //第二部就是以这个新改的点为中心开始上下左右去搜索是否那些位置符合(且要进行边界的判断)
        for(int i = 0;i<4;++i)
        {
            //这里也就算出来了每一个发散出来的4个方向新的坐标
            int newx = sr + nextposition[i][0];
            int newy = sc + nextposition[i][1];
            //判断有效性
            //如果不满足条件,那就不需要进行下面的步骤,直接计算下一个新的点
            if(newx <0 || newx >= row || newy < 0 || newy >= col)
                continue;
            //走到这里就说明他是具有合法性的
            if(image[newx][newy] == oldColor && visited[newx][newy] == 0)
            {
                DFS(image,visited,row,col,newx,newy,oldColor,newColor);
            }
        }
    }
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
        int row = image.size();
        int col = image[0].size();
        vector<vector<int>> visited(row,vector<int>(col,0));//表示每一个位置都是没有被访问过的
        int oldColor = image[sr][sc];
        DFS(image,visited,row,col,sr,sc,oldColor,newColor);
        return image;
    }
};

2.1. LeetCode463题—岛屿的周长

链接: https://leetcode-cn.com/problems/island-perimeter/
在这里插入图片描述
解题思路:
对于一个陆地格子的每条边,它被算作岛屿的周长当且仅当这条边为网格的边界或者相邻的另一个格子为水域。 因此,我们可以遍历每个陆地格子,看其四个方向是否为边界或者水域,如果是,将这条边的贡献1

class Solution {
public:
    int dfs(vector<vector<int>>& grid,int i,int j,int row,int col)
    {
        if(i < 0 || i >= row || j < 0 || j >= col)
            return 1;
        if(grid[i][j] == 0)
            return 1;
        if(grid[i][j] == 2)
            return 0;
        grid[i][j] = 2; //表示该点已经访问过了
        //要的是前后左右所提供的贡献
        return dfs(grid,i-1,j,row,col) + dfs(grid,i,j-1,row,col) + dfs(grid,i+1,j,row,col) + dfs(grid,i,j+1,row,col);
    }
    int islandPerimeter(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        int sum = 0;
        for(int i = 0;i<row;i++)
        {
            for(int j = 0;j<col;j++)
            {
                //遍历整个二维地图,如果找到陆地,就进行接下来的操作
                if(grid[i][j] == 1)
                {
//这个dfs相当于算出来当前这个陆地他的四个方向上所能给他周长的贡献,然后遍历每一块陆地都算出来的累加和,就是周长
                    sum += dfs(grid,i,j,row,col); 
                }
            }
        }
        return sum;
    }
};

解法2:迭代法好像更加能够使人理解(这到题强烈推荐方法)

int nextposition[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
class Solution {
public:
    //这道题用迭代的方法好像就会好很多
    //但是迭代就不怕算已经遍历过的吗?
    int islandPerimeter(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        int sum = 0;
        for(int i = 0;i<row;++i)
        {
            for(int j = 0;j<col;++j)
            {
                if(grid[i][j] == 1)
                {
                    int ret = 0;
                    for(int k = 0;k<4;++k)
                    {
                        int x = i + nextposition[k][0];
                        int y = j + nextposition[k][1];
                        if(x <0 || x>=row || y < 0 || y>= col || grid[x][y] == 0)
                            ret += 1;
                    }
                    sum += ret;
                }
            }
        }
        return sum;
    }
};

2.2 LeetCode200题—岛屿数量

链接: https://leetcode-cn.com/problems/number-of-islands/
在这里插入图片描述
题解:这道题和渲染的思想如出一辙,基本一致(只是这里并不需要修改连接在一起的点颜色)

int nextposition[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
class Solution {
public:
    void dfs(vector<vector<char>>& grid,vector<vector<int>>& visited,int row,int col,int i,int j)
    {
        //以第一个示例为例子,在这里进行解说,等这里都走完了之后,就会发现将所有连在一起的'1'都进行了渲染
        visited[i][j] = 1;
        for(int k = 0;k<4;++k)
        {
            int x = i + nextposition[k][0];
            int y = j + nextposition[k][1];
            if(x < 0 || x >= row || y < 0 || y >= col)
                continue;
            if(grid[x][y] == '1' && visited[x][y] == 0)
            {
                dfs(grid,visited,row,col,x,y);
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        //初始化0表示所有的点都没有拜访过,1则表示这个点已经访问过了
        vector<vector<int>> visited(row,vector<int>(col,0));
        int num = 0;
        for(int i = 0;i<row;++i)
        {
            for(int j = 0;j<col;++j)
            {
                if(grid[i][j] == '1' && visited[i][j] == 0)
                {
                    num++;
                    dfs(grid,visited,row,col,i,j);
                }
            }
        }
        return num;
    }
};

2.3 LeetCode695题—岛屿的最大面积

链接: https://leetcode-cn.com/problems/max-area-of-island/
在这里插入图片描述
题解:同样的思想,相当于找到岛屿的数量题

int nextposition[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
class Solution {
public:
    void dfs(vector<vector<int>>& grid,vector<vector<int>>& visited,int row,int col,int i,int j,int& sum)
    {
        sum += 1;
        visited[i][j] = 1;
        for(int k = 0;k<4;++k)
        {
            int x = i + nextposition[k][0];
            int y = j + nextposition[k][1];
            if(x < 0 || x >= row || y < 0 || y >= col)
                continue;
            if(grid[x][y] == 1 && visited[x][y] == 0)
            {
                dfs(grid,visited,row,col,x,y,sum);
            }
        }
    }
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        vector<vector<int>> visited(row,vector<int>(col,0));
        int max = 0;
        for(int i = 0;i<row;++i)
        {
            for(int j = 0;j<col;++j)
            {
                if(grid[i][j] == 1 && visited[i][j] == 0)
                {
                    int sum = 0;
                    dfs(grid,visited,row,col,i,j,sum);
                    max = std::max(max,sum);
                }
            }
        }
        return max;
    }
};

3. LeetCode130题—被围绕的区域

链接: https://leetcode-cn.com/problems/surrounded-regions/
在这里插入图片描述
题解:可以从相反的思路下手,我们想要找完全被‘X’包围的’O’,但是会发现并不好找,那么我们就找没有被’X’完全包围的’O’,边界上的’O’一定没有被’X’完全包围,那么和边界上的这个’O’相连的‘O’肯定都不满足

int nextposition[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
class Solution {
public:
    //必须是4个方向都包围的(完全封闭的),才能算作是被包围的,才能够替换
    //这道题的解法优点向图像渲染,只不过这道题需要首先是从反方向思考下手
    //我去找那些完全被X包围的o是不好找的,但是我去找边上的o,以及和他相连的o那么这些o就是都不能够被替换的,只需要在这个过程中做好特殊标记就型
    void dfs(vector<vector<char>>& board,int i,int j,int row,int col)
    {
        board[i][j] = '*';
        for(int k = 0;k<4;++k)
        {
            int x=  i + nextposition[k][0];
            int y = j + nextposition[k][1];
            if(x <0 || x>= row || y < 0 || y>= col)
                continue;
            //走到这里说明是合法的点,那么就要判断这个点是都也是'O' 且没有被渲染过
            if(board[x][y] == 'O' && board[x][y] != '*')
                dfs(board,x,y,row,col);
        }
    }
    void solve(vector<vector<char>>& board) {
        int row = board.size();
        int col = board[0].size();
        //找第一行和最后一行中的'0'
        for(int j = 0;j<col;++j)
        {
            if(board[0][j] == 'O')
                dfs(board,0,j,row,col);
            if(board[row-1][j] == 'O')
                dfs(board,row-1,j,row,col);
        }
        //找第一列和最后一列中的'O'
        for(int i = 0;i<row;++i)
        {
            if(board[i][0] == 'O')
                dfs(board,i,0,row,col);
            if(board[i][col-1] == 'O')
                dfs(board,i,col-1,row,col);
        }

        //上面也就把所有没有被X完全包围的'O'都修改为了'*'
        for(int i = 0;i<row;i++)
        {
            for(int j = 0;j<col;j++)
            {
                //此时的'O'是完全被包围的
                if(board[i][j] == 'O')
                    board[i][j] = 'X';
                if(board[i][j] == '*')
                    board[i][j] = 'O';
            }
        }
    }
};

4. 剑指offer13题—机器人的运动范围

链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/在这里插入图片描述

class Solution {
public:
    int get(int x)
    {
        int ret = 0;
        while(x)
        {
            ret += (x % 10);
            x /= 10;
        }
        return ret;
    }
    void  DFS(int i,int j,int m,int n,int k,int& count,vector<vector<int>>& visited)
    {
        if(i < 0 || i>= m || j < 0 || j >= n || get(i) + get(j) > k || visited[i][j] == 1)
            return;
        count++;
        visited[i][j] = 1;
        //然后再处理他的四个方向
        DFS(i+1,j,m,n,k,count,visited);
        DFS(i-1,j,m,n,k,count,visited);
        DFS(i,j-1,m,n,k,count,visited); 
        DFS(i,j+1,m,n,k,count,visited); 
    }
    //这道题可以理解为他能满足的走的格子
    int movingCount(int m, int n, int k) {
        vector<vector<int>> visited(m,vector<int>(n,0));
        int count = 0;
        DFS(0,0,m,n,k,count,visited);
        return count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * (有向)图的深度优先遍历算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍历过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍历算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通图) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值