图的遍历(DFS和BFS)

图的遍历算法是求解图的连通性问题(最小生成树问题),拓扑排序(判定AOV网是否存在环)和关键路径(估算工程完成的时间)等算法的基础
和树的通历类似,图的遍历也是从图中某一顶点出发,按照某种方选对图中所有演道进行图的遍历算法是求解图的连通性问题、拓扑排序和关键路径等算法的基础——
然而,图的遍历要比树的通历复杂得多。因为图的任一顶点都可能和其系的项点制所以在访问了某个顶点之后,可能沿着某条路径搜索之后,又回到该顶点上。为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的项点,为此设一个辅助数组visited[n],其初始价置为“false”或者0,一旦访问了顶点,就变为“true”或者1。
根据搜索路径的方向,通常有两条遍历图的路径:深度优先搜索和广度优先搜索。它们对无向图和有向图都适用。 

深度优先遍历DFS:仿树的先序遍历

深度优先搜索(Deplh First Search,DFS)遍历类似于树的先序遍历,是树的先序遍历的推广。对于一个连通图,深度优先搜索遍历的过程如下。
(1)从图中某个顶点v出发,访问v。
(2)找出刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点.重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。
(3)返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个来被动问的邻接点,访问该顶点。
(4)重复步骤(2)和步骤(3),直至图中所有顶点都被访问过,搜索结束。

深度优先搜索遍历的算法实现
显然,深度优先搜索遍历连通图是一个递归的过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组visited[n],其初值为“false”,一旦某个顶点被访问,则其相应的分量置为“true”。
【算法步骤】
①从图中某个顶点v出发,访问v,并置visited[的值为true。
②依次检查v的所有邻接点w,如果visited[w的值为false,再从w出发进行
递归遍历,直到图中所有顶点都被访问过。 

//深度优先搜索遍历连通图 G是连通图
void DFS(Graph G,int v)
{
    int w;//邻接点  数据类型  表头结点类型
    cout<<v;visited[v]=true;
    for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))
    {
        if(!visited[w])  DFS(G,w);
    }
}


//深度优先搜索遍历非连通图  非连通图。。。。。。
void DFSTraverse(AMGraph G)
{
    int v;//
    for(v=0;v<G.vexnum;++v)
    {
        visited[v]=false;
    }
    for(v=0;v<G.vexnum;++v)
    {
        if(!visited[v]) DFS(G,v);//每调用一次DFS,就遍历一个连通分量,有多少次调用,就说明图中
    }
}

深度遍历连通图和非连通图传参都是不一样的,连通图可以通过一个顶点遍历到所有顶点,因为是连通的。但是非连通图不行,通过一个顶点到达不了所有顶点。

    for(v=0;v<G.vexnum;++v)
    {
        if(!visited[v]) DFS(G,v);//每调用一次DFS,就遍历一个连通分量,有多少次调用,就说明图中
    }

这个for循环是深度遍历非连通图的关键,这个循环条件可以保证遍历到每一个顶点。由于是非连通图,不可能通过一个顶点到达所有。

for循环中的if条件又满足了深度遍历,未被访问过的调用“深度遍历连通图”,进行深度遍历。

只有符合if的执行条件才会调用“深度遍历连通图”,是所以可以知道每调用一次,就会遍历一个连通分量,有多少次调用,就说明这个非连通图中有多少个连通分量。(非连通图中的连通分量是连通图)

广度优先遍历BFS:仿树的层次遍历

 

//广度优先搜索遍历连通图

/*初始化一个空队列*/
Status InitQueue(SqQueue *Q)
{
    Q->front= 0;
    Q->rear=0;
    return OK;
}

Status EnQueue(SqQueue *Q,QElemType e) {
    if ((Q->rear + 1) % MAXSIZE == Q->front) /*队列满的判断*/
        return ERROR;
    Q->data[Q->rear] = e;  //将元素e赋值给队尾
    Q->rear = (Q->rear + 1) % MAXSIZE; //rear指针向后移一个位置,在末尾则转到数组头部
}

Status DeQueue(SqQueue *Q,QElemType *e)
{
    if (Q->front == Q->rear)
        return ERROR;
    *e= Q->data[Q->front]; //将队头元素赋值给e
    Q->front=(Q->front+1)%MAXSIZE; //front指针向后移一位置,若到最后转到数组头部
}

//判空
bool Queueempty(SqQueue *Q)
{
    return Q->front==Q->rear;
}


//广度优先搜索连通图
void BFS(Graph G,int v)
{
    int u;//数据类型
    int w;

    cout<<v;visited[v]=true;//一经访问就置为true

    InitQueue(Q);
    EnQueue(Q,v);//入队
    
//上述都是初始化
//while循环让遍历一直进行

    while(!Queueempty(Q))//只要队列不空=只要还没有访问完所有顶点,就一直进行
    {
        DeQueue(Q,u);//出队//访问过  已操作

        for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))//依次检查所有邻接点。。u是第一个邻接点,w是u的下一个邻接点。。。
        {
            if(!visited[w])//未被访问的邻接点  访问,标记,入队
            {
                cout<<w;visited[w]=true;//操作(根据需求)  标记

                EnQueue(Q,w);//入队//把u的所有未访问过的邻接点都入队//u的下一层都入队//每次入队的都是一层
            }  
        }
    }
    
}
//入队的一层为下一次操作做准备,出队的是这一次进行“操作”的,具体做什么操作根据你的需要而定。
//每次循环,出队一个,访问它的所有邻接点,访问一个入队一个(所有这一层级的都入队)
//队列具有“先进先出”的特点,保证分层

若是非连通图,由于“非连通”,那么从某个顶点出发,进行访问,直到图中所有顶点都被访问过。之后,图中一定还有顶点未被访问。 需要从图中另选一个未被访问的顶点作为起始点。 重复“深度优先搜索遍历连通图”,直到图中所有顶点都被访问(visited都为true 停止执行)

FirstAdjVex和NextAdjVex函数 在邻接矩阵或者邻接表中查找(对比) 如果图的存储结构不同,这两个操作的实现方法不同,时间耗费也不同。 FirstAdjVex(G,v)返回v的第一个邻接点,如果没有则返回空。 NextAdjVex(G,v,w)返回v的(相对于w的)下一个邻接点。如果w是v的最后一个邻接点,返回空。 如果是邻接矩阵,FirstAdjVex(G,v)返回的就是[v-1][第一个不为0的列],第v-1行的非0的(或者非MaxIn的)都是该顶点的邻接点。NextAdjVex(G,v,w)除了第一个不为0的下一个不为0的如果是邻接表(由表头数组构成的多个单链表,FirstAdjVex(G,v)其中v就是表头,在表头数组中寻找到v,这样就找到了这个单链表, 单链表中的边结点都是该顶点的邻接点,表头的firstarc指针域就是该顶点的第一个邻接点,每个边结点的nextarc是下一个邻接点

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值