vlambda博客
学习文章列表

算法周刊 | Week10 深度优先搜索&广度优先搜索


算法周刊

Week10

2021.9.25


1. 深度优先搜索介绍

图的深度优先搜索(Depth First Search),和树的先序遍历比较类似。

它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

显然,深度优先搜索是一个递归的过程。


2. 深度优先搜索图解

2.1 无向图的深度优先搜

下面以"无向图"为例,来对深度优先搜索进行演示。

算法周刊 | Week10 深度优先搜索&广度优先搜索

对上面的图G1进行深度优先遍历,从顶点A开始。

算法周刊 | Week10 深度优先搜索&广度优先搜索

第1步:访问A。

第2步:访问(A的邻接点)C。在第1步访问A之后,接下来应该访问的是A的邻接点,即"C,D,F"中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,C在"D和F"的前面,因此,先访问C。

第3步:访问(C的邻接点)B。在第2步访问C之后,接下来应该访问C的邻接点,即"B和D"中一个(A已经被访问过,就不算在内)。而由于B在D之前,先访问B。

第4步:访问(C的邻接点)D。在第3步访问了C的邻接点B之后,B没有未被访问的邻接点;因此,返回到访问C的另一个邻接点D。

第5步:访问(A的邻接点)F。前面已经访问了A,并且访问完了"A的邻接点B的所有邻接点(包括递归的邻接点在内)";因此,此时返回到访问A的另一个邻接点F。

第6步:访问(F的邻接点)G。

第7步:访问(G的邻接点)E。

因此访问顺序是:A -> C -> B -> D -> F -> G -> E

2.2 有向图的深度优先搜索

下面以"有向图"为例,来对深度优先搜索进行演示。

算法周刊 | Week10 深度优先搜索&广度优先搜索

对上面的图G2进行深度优先遍历,从顶点A开始。

算法周刊 | Week10 深度优先搜索&广度优先搜索

第1步:访问A。

第2步:访问B。在访问了A之后,接下来应该访问的是A的出边的另一个顶点,即顶点B。

第3步:访问C。在访问了B之后,接下来应该访问的是B的出边的另一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,因此先访问C。

第4步:访问E。接下来访问C的出边的另一个顶点,即顶点E。

第5步:访问D。接下来访问E的出边的另一个顶点,即顶点B,D。顶点B已经被访问过,因此访问顶点D。

第6步:访问F。接下应该回溯"访问A的出边的另一个顶点F"。

第7步:访问G。

因此访问顺序是:A -> B -> C -> E -> D -> F -> G


3.例题:洛谷的P1451求细胞数量

算法周刊 | Week10 深度优先搜索&广度优先搜索

思路就是搞一个数组,不为0的地方就是细胞,然后dfs搜连通块,把搜到的都归0,保证不重复然后就是循环找没被归0的,答案+1。

#include <bits/stdc++.h>using namespace std;int n,m,ans,dx[]={-1,0,1,0},dy[]={0,-1,0,1},a[105][105];//a即地图,dx和dy方向增量数组就不用我讲了吧void dfs(int x,int y){    if (x>n||y>m||x<0||y<0)return;    a[x][y]=0;//标记为没有    for (int i=0;i<4;i++)if (a[x+dx[i]][y+dy[i]])dfs(x+dx[i],y+dy[i]);//如果有才搜}//搜索int main(){    scanf("%d%d",&n,&m);    for (int i=0;i<n;i++)    for (int j=0;j<m;j++)scanf("%1d",&a[i][j]);//只读1位    for (int i=0;i<n;i++)    for (int j=0;j<m;j++)if (a[i][j])ans++,dfs(i,j);//找    printf("%d",ans);//输出    return 0;}


4. 广度优先搜索介绍

广度优先搜索算法(Breadth-First Search,BFS)是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。BFS并不使用经验法则算法。

广度优先搜索让你能够找出两样东西之间的最短距离,不过最短距离的含义有很多!

像这样的,求问从A到B的最短路径的结点数是多少?

5.基本思想和思路

1.根据路是不是相同的,我们可以创建一个布尔型二维数组way,way[w1][w2]=1代表从w1可以到达w2,=0时则代表不能, 设A的标志为0,其他的依次往后推。

Bool way[6][6]={ {0,0,1,1,1,0},{0,0,0,0,0,0},{0,1,0,0,0,0},{0,0,0,0,0,1},{0,0,0,0,0,1},{0,1,0,0,0,0}};

2.创建一个step数组,初始值为0,step[0]代表A的结点数,step[1]代表B的结点数,A有一结点所以开始假设step[0]=1,储存列出了几层子结点,也就是目前所有路径的结点数

创建一个队列,比如叫que(这里只是简单的设了个数组)

3.在队列que中加入A结点的标志0,步骤是将tail+=1并且que[tail]=0

然后搜索A的子结点,例如这样:

循环(i=0;i<=5;i++)//因为共有6个结点

如果(way[que[head+1]][i]==1)把子结点加入队列;

当循环完毕以后呢,就等于把A的子结点全找出来了,列出了一整层结点,删去第一个结点,step[2]和[3]和[4]都=step[0]+1,head-=1,那样head指的就是A的子结点C(标志2)了如图

4.继续判断,搜索2的子结点,并且加入队列,再删去2,搜索3的子结点……直到最后tail指针指的是B(标志1),则输出step[1](B的标志是1),就是最短路径数。如果head=tail了,说明队列为空,而且没找到答案,所以就没有可以到达目标结点的路,输出什么的就随你了。

#include <stdio.h>#include <string.h>Bool way[6][6]={{0,0,1,1,1,0},{0,0,0,0,0,0},{0,1,0,0,0,0},{0,0,0,0,0,1},{0,0,0,0,0,1},{0,1,0,0,0,0},};int main(){    int que[101],head=0,tail=1,i,step[6];    memset(step,0,sizeof(step));//初始化步数数组    que[1]=0;//初始化队首    step[0]=1; //结点A的步数为1    do    {        for(i=0;i<=5;i++)        {            if(way[que[head+1]][i]==1)            {                tail++;                que[tail]=i;                step[i]=step[que[head+1]]+1;                if(que[tail]==1)                {                    printf("%d",step[1]);                    return 0;                }            }        }        head++;    }while(head<tail);//当队列不为空    printf("no answer!");    return 0;}



邮箱|[email protected]