这周的主题是基本的搜索算法,基于bfs(广度搜索)和dfs(深度搜索)来实现的
在这里简单介绍以下dfs(深度搜索)算法
这里的关键点是递归和回溯
深度优先搜索算法(DFS)是一种用于遍历或者搜索树或图的算法,沿着一些树遍历树的结点,尽可能深的搜索树的分支,当节点的所在边都被探索过,搜索将回溯到发现结点v的那条边的起始结点.这一过程持续到发现原节点可以到达的所有节点位置。
深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义,深度优先,则是以深度为准则,先一条路走到底,直到达到目标。这里称之为递归下去。
否则既没有达到目标又无路可走了,那么则退回到上一步的状态,走其他路。这便是回溯上来。
下面来结合具体例子来进行理解
假设按照以下的顺序来搜索:
1.V0->V1->V4,此时到底尽头,仍然到不了V6,于是原路返回到V1去搜索其他路径;
2.返回到V1后既搜索V2,于是搜索路径是V0->V1->V2->V6,,找到目标节点,返回有解。
这样搜索只是2步就到达了,但是如果用BFS的话就需要多几步。
DFS的核心伪代码(C语言递归方式)
//它的前置条件是标记已经走过的路径,让book全部初始化为0
//并且知道起始状态和结束状态
void dfs(int step)
{
for(i = 1; i <= n ;i++)
{
if(book[i] == 0)
{
a[step] = i; //a数组已经实现
book[i]= 1; //将book数组设为1,表示已经走过
dfs(step+1);//这里通过函数递归的方式,来尝试下一个节点
book[i] =0 ;//这条语句和上条语句很重要,是dfs的关键,回溯的思想,dfs通过递归调用自己,来进行下一步的尝试,如果可以就开始执行下一部分
}
return ;
}
DFS很难保存最短路径,它采用了回溯的方式来进行,一条路经走到头,直到不能走的时候然后返回上次,DFS一般用来求有几种方式阿什么的问题,最值问题一般来使用BFS来进行求解的
DFS另一个有用的性质是: 对于二叉树而言, dfs得到的节点顺序正是其前序遍历(preorder traversal)的顺序.
其实前序遍历的定义就相当于是一个递归版本的dfs了:
[preorder(node)] = node.val + [preorder(node.left)] + [preorder(node.right)]
DFS有两种实现方式,一种是利用栈来实现,另外一种是通过递归的方式来实现
接下来,简单说一下BFS算法(广度优先算法)
BFS算法一般是需要和队列结合起来使用的,是一种盲目的搜索法,同时也叫横向优先算法,它把每个节点能访问到的所有位置以队列的形式存储,放入到这个容器中
首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
在这里也以例子来进行解释一下
如果说以v0点作为开始,那么在第一次执行操作之后应该在队列中存储v1,v2,v3这三种方式,从v0中选择一种,如果选择v3接下来应该存储的是v0,v1,v5这三种,但是因为之前已经做过标记,如果在这里仅仅存储的是v5
实现方式:
1.首先取起始条件放入到队列中,
2.从队列中取出队首节点,检测它是不是目标
如果找到目标,就结束搜索并且向主函数传达结果
不然的话,进行出队
3 如果队列为空,说明图检查过没有结果
下面给出C语言实现方式
int book[1001],que[1001],head,tail;//head和end模拟队列的队首和队尾位置
head=1;
tail=1;
//从1号顶点出发,将1号顶点加入队列
que[tail]=1;
tail++;
book[1]=1;//标记1号顶点已访问
//BFS核心部分
while(head<tail)//当队列不为空的时候循环
{
cur=que[head];
for(i=1;i<=n;i++)
{
//判断从顶点cur到顶点i是否有边,并判断顶点i是否已经被访问过
if(e[cur][i]!=INT_MAX && book[i]==0)
{
//将顶点i加入队列
que[tail]=i;
tail++;
book[i]=1;
}
//如果tail大于n,说明所有顶点都被访问过
if(tail>n)
{
break;
}
}
//当一个顶点拓展结束后,要将head++,相当于从队首删除该顶点
head++;
}
下章开始写这周的算法题