搜索
深度优先搜索(DFS)
深度优先搜索是使用递归的方式以深度为主逐个探索遍历每种情况,在排列组合,迷宫问题中十分常用。深度优先搜索思想简单,但是由于使用递归,要求我们遍历时探索的必须深度有限。不然有可能会使栈溢出。还要注意有时我们在使用深度优先搜索时情况过多,而大部分是无用解时就需要套入剪枝。
模型:1
2
3
4
5
6DFS()
{
//1.判断边界,如果已经到达搜索的最深,则回退尝试其他可能
//2.尝试当下的每一种可能
//3.确定一种可能后,继续下一步
}
例1 员工的重要度
力扣:
https://leetcode-cn.com/problems/employee-importance/submissions/
解析
抽象模型。1
2
3
4
5
6DFS(id)
{
//1.获取当前员工的重要度
//2.累加每一个下属(for)的重要度,DFS(下属id)
//3.return累加的重要度
}
实现
1 | class Solution { |
例2 图像渲染
力扣:
https://leetcode-cn.com/problems/flood-fill/
解析
抽象模型。1
2
3
4
5
6
7
8
9
10
11DFS()
{
//1.nx,ny染色
//2.处理上,下,左,右4个点
{
//以深度优先逐个方向遍历周边4个点
//新位置颜色符合,且没有越界,且没有染过色
//符合条件处理新位置
DFS(新的位置)
}
}
实现
1 | static int nextP[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; |
例3 走迷宫
牛客:
https://www.nowcoder.com/questionTerminal/6276dbbda7094978b0e9ebb183ba37b9
解析
模型:1
2
3
4
5
6DFS()
{
//1.当前位置已走,路径长度+1,并且置为墙表示走过了
//2.走到终点,和最短路径长度比较取最优
//3.遍历四周,如果不是墙且不越界则遍历
}
但是,这种模型在一张只有极少障碍物的迷宫中要消耗很多的时间,因为我们要让每一个结点都被遍历近10次,这其中会消耗大量时间,并且很多是无用的解。因此我们在其中可以加入动态规划的思想。
模型:1
2
3
4
5DFS()
{
//1.遍历四周,如果不是墙且不越界则进行二次判断
//2.如果本次路径到达这个位置的路径数要小于以往在表中记录的路径数,则将表中最优解进行修改,否则不再遍历此位置。
}
实现
1 | #include <iostream> |
广度优先搜索(BFS)
广度优先搜索是以广度为主逐层往外一次遍历,不同于深度优先逐条解进行排列组合,广度优先搜索只会遍历一次所有结点,并且会最先拿到最优解,因此某些情况下广度优先搜索效率优于深度优先搜索。广度优先搜索在迷宫问题的解上效率会更高,在寻找最优解时会更快,因为其不需要列出所有组合的情况。
广度优先搜索在实现时需要借助队列,每一次将下一层要遍历的结点入队,上一层出队。
例1 员工的重要性
力扣:
https://leetcode-cn.com/problems/employee-importance/
解析
将要查找的第一个员工入队,之后每次遍历队中元素将其重要度相加,并且将其下属入队。
实现
1 | /* |
例2 N叉树层序遍历
力扣:
https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/submissions/
解析
建立队列,将根节点入队,创造循环遍历队中每个结点将其孩子入队,但这题要注意要分树的每一行进行遍历,先遍历这一行的结点将其放到结果数组中,再以此遍历下一行的结点。
实现
1 | /* |
例3 腐烂的橘子
力扣:
https://leetcode-cn.com/problems/rotting-oranges/
解析
类似于N叉树的遍历。每一分钟腐烂一层,遍历每一层即可,最后搜索结束后判断是否还有新鲜橘子。
实现
1 | int nextP[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; |