1、回溯算法介绍
回溯算法主要解决组合问题、分割问题、子集、排列、棋盘(N皇后、解数独)等问题,主要是通过DFS(深度优先搜索)穷举所有可能性,递归得到问题的所有解,他比暴力枚举更加厉害在回溯(即遇到不符合的状态 会回到上一个状态)、剪枝(剪掉不符合的状态);这样处理过后时间和空间复杂度都大大降低,而且逻辑清晰;
2、回溯法的逻辑和模板
根据树的结构我们可以利用for循环进行横向遍历,然后通过递归进行纵向遍历。
回溯模板伪代码
void backtracking{参数}{
if {终止条件} {
存放结果
return
}
for {
选择叶子节点 进行处理;
backtrack{路径,选择列表}//递归的实现,其中backtracking 就是自己调用自己
回溯 撤销处理结果;//意味着可以将入栈中不符合条件的状态移除
}
回溯算法和人一样,一条路走到黑,走不通就返回,走的通就记录,相对于BFS,把下一步能走的状态都加入队列来说,DFS从根节点出发没深度探索空间树,更加高效的解决排列和组合问题,因此是一种通用算法;
3、深度优先搜索(DFS)
深度优先搜索为一条路走到黑的搜索,利用递归和剪枝的方法来解决排列组合问题,在做DFS时,我们一般先将二叉树用手画出,这样方便我们理解算法实现的路径和逻辑。
从根节点开始递归遍历,然后先走左节点,直到找到目标节点或者下一节点为空,这时就要回溯到上一节点,也就是回到过去走过的节点,只有这一子树下面节点都走过了,才能选择另一个子树。
深搜模板
int search(int t)
{
if(满足输出条件)
{ 输出解 }
else
{ for (int i=1;i<=集合的数目;i++)
{
if(满足进一步搜索的条件)
{ 为进一步搜索所需要的状态打上标记;
search(t+1);
恢复到打标记以前的状态;//也就是说{回溯一步}
}
}
}
总结:DFS 先画出二叉树的图;进行图的遍历,最后产生多少个解;回溯是DFS+剪枝;是树的遍历,其实大差不差,很相似。
4、广度优先搜索(BFS)
BFS(广度优先搜索)是一种贪心算法的典型应用,如果形象的说DFS是一条路走到黑,那么BFS就是横向遍历,将下一步所有的选择都进行考虑,进行下一步是由上一步决定的,每走一步都是在做最好的选择,当找到目标节点是,自动更新最短距离,所以说虽然广度优先搜索,把每一层的选择都进行了考虑,但只有在最短路径的选择上才会记录距离,那么局部最优到整体最优,BFS就解决了最短路径的问题;
常见最短路径问题:走迷宫 扫雷等;
BFS答题模板
#准备:队列,标记数组,距离数组
queue=deque()
vis=[-1]*(100010) 用来做标记
dis=[-1]*(100010) 用来更新距离
#1.起点入队列,打标记,更新距离
queue.append(n)
vis[n]=1
dis[n]=0
#2、判断队列是否为空
while len(queue) != 0:
# 1 取出队首u
u=queue.popleft() 队列从左边弹出
#2 u和终点相同,直接弹出
if u==k: break; 找到了目标节点
#3 访问与u相邻的v节点,这是要看题目条件
for v in []:
#如果v未标记,就入队列,打标记,更新距离
#v要满足未越界,未标记,未障碍物 先判断是不是越界,这样有利于节省空间
if 1<=v<=100000 and vis[v]==-1:
queue.append(v)
vis[v]=1
dis[v]=dis[u]+1
print(dis[k])
区别:BFS是利用队列queue(先进先出)的特性实现的,而DFS是利用栈(先进后出)的特性实现的,因此两者一个是横向遍历,把每一步所有的选择都加入队列考虑,不断入队出队,寻求目标节点,进行BFS,而另一个是利用递归的思想,一条路走到黑,把每一条路都走完在回溯,走另一条路,所以二者底层逻辑不一,解决的问题也不一样