深度优先搜索
遍历每一个点都是一条道走到黑才停止下来,所以也被称为深度优先搜索
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/
题解:
对于这道题需要一个辅助数组,这个数组用来记录每个点是否有被访问过。
- 第一步就是修改题目中所要渲染的点
- 然后以这个点为中心去向他的前后左右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;
}
};