学习随笔#8 深度优先算法和广度优先算法

深度优先算法和广度优先算法

图的基本概念

  由结点和连接每对结点的边所构成的图形就是图。图可以有以下分类:

  1. 如果给边加上一个值表示权重,这种图就是加权图,没有权重的图就是非加权图,没有权重的边只能表示两个节点的连接状态,而有权重的边就可以表示节点之间的“连接程度”。
  2. 如果给边加上表示方向的箭头,即表示节点之间的传递方向,这种图就是有向图,而边上没有箭头的图就是无向图。
  3. 如果图中任意两个节点都能找到路径可以将它们进行连接,则称该图为连通图,上面这个图就是连通图,反之则为非连通图。
      和图相关的算法主要有两类:
  • 图的搜索算法:图的搜索指的就是从图的某一节点开始,通过边到达不同的节点,最终找到目标节点的过程。根据搜索的顺序不同,图的搜索算法可分为“广度优先搜索”和“深度优先搜索”两种。
  • 图的最短路径问题:最短路径问题就是要在两个节点的所有路径中,找到一条所经过的边的权重总和最小的路径。相关算法有“贝尔曼-福特算法”,“狄克斯特拉算法”和“A* 算法”三种。

  图的存储结构有两种,一种是基于二维数组的邻接矩阵表示法,另一种是基于链表的邻接法。
  在邻接矩阵中,可以如下表示顶点和边连接关系:

图的邻接矩阵表示法
  将顶点对应为下标,矩阵中元素的1表示两结点相连,0表示不相连。如图G5中,结点0和1、2、3都相连,所以矩阵的第一列依次为0、1、1、1。而结点2只和0、1相连,所以矩阵的第三列依次为1、1、0、0。图的邻接矩阵都是对称矩阵。

深度优先算法

  深度优先算法是一种基于邻接矩阵的遍历算法。遍历是指从图的某个顶点出发,访问图中的所有顶点,且使每个顶点尽被访问一次。在深度优先搜索中,保存候补节点是栈,栈的性质就是先进后出,即最先进入该栈的候补节点就最后进行搜索。
深度优先搜索思路

  1. 访问顶点v;
  2. 依次从v未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
  3. 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。如:

在这里插入图片描述

  1. 首先输出 V1,标记V1的flag=true;
  2. 获得V1的邻接边 [V2 V3],取出V2,标记V2的flag=true;
  3. 获得V2的邻接边[V1 V4 V5],过滤掉已经flag的,取出V4,标记V4的flag=true;
  4. 获得V4的邻接边[V2 V8],过滤掉已经flag的,取出V8,标记V8的flag=true;
  5. 获得V8的邻接边[V4 V5],过滤掉已经flag的,取出V5,标记V5的flag=true;
  6. 此时发现V5的所有邻接边都已经被flag了,所以需要回溯。(左边黑色虚线,回溯到V1,回溯就是下层递归结束往回返)
  7. 回溯到V1,在前面取出的是V2,现在取出V3,标记V3的flag=true;
  8. 获得V3的邻接边[V1 V6 V7],过滤掉已经flag的,取出V6,标记V6的flag=true;
  9. 获得V6的邻接边[V3 V7],过滤掉已经flag的,取出V7,标记V7的flag=true;
  10. 此时发现V7的所有邻接边都已经被flag了,所以需要回溯。(右边黑色虚线,回溯到V1,回溯就是下层递归结束往回返)。

代码

bool visited[MaxVnum];
void DFS(Graph G,int v)
{
    visited[v]= true; //从V开始访问,flag它
    printf("%d",v);    //打印出V
    for(int j=0;j<G.vexnum;j++) 
        if(G.arcs[v][j]==1&&visited[j]== false) //这里可以获得V未访问过的邻接点
            DFS(G,j); //递归调用,如果所有节点都被访问过,就回溯,而不再调用这里的DFS
}

void DFSTraverse(Graph G) {
    for (int v = 0; v < G.vexnum; v++)
        visited[v] = false; //刚开始都没有被访问过

    for (int v = 0; v < G.vexnum; ++v)
        if (visited[v] == false) //从没有访问过的第一个元素来遍历图
            DFS(G, v);
}

广度优先算法

  广度优先算法就是层层向下遍历。广度优先搜索会根据离起点的距离,按照从近到远的顺序对各节点进行搜索。而深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条路径。
  在广度优先搜索中,有一个保存候补节点的队列,队列的性质就是先进先出,即先进入该队列的候补节点就先进行搜索。若按广度优先遍历算法,则顺序为v1、v2、v3、v4、v5、v6、v7、v8。
在这里插入图片描述
广度优先搜索思路

  1. 访问顶点vi ;
  2. 访问vi 的所有未被访问的邻接点w1 ,w2 , …wk ;
  3. 依次从这些邻接点(在步骤②中访问的顶点)出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问;

说明
  为实现3,需要保存在步骤②中访问的顶点,而且访问这些顶点的邻接点的顺序为:先保存的顶点,其邻接点先被访问。 这里我们就想到了用标准模板库中的queue队列来实现这种先进现出的服务。

  • 将V1加入队列,取出V1,并标记为true(即已经访问),将其邻接点加进入队列,则 <—[V2 V3]
  • 取出V2,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[V3 V4 V5]
  • 取出V3,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[V4 V5 V6 V7]
  • 取出V4,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[V5 V6 V7 V8]
  • 取出V5,并标记为true(即已经访问),因为其邻接点已经加入队列,则 <—[V6 V7 V8]
  • 取出V6,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[V7 V8]
  • 取出V7,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[V8]
  • 取出V8,并标记为true(即已经访问),将其未访问过的邻接点加进入队列,则 <—[]
    代码
#include <queue>
using namespace std;
....
void BFSTraverse(Graph G)
{
    for (int v=0;v<G.vexnum;v++) //先将其所有顶点都设为未访问状态
        visited[v]=false;
    queue<int> Q;
    for(int v=0;v<G.vexnum;v++)    
    {
        if(visited[v]==false)   //若该点没有访问
        {
            Q.push(v);    //将其加入到队列中
            visited[v]=true;
            while (!Q.empty())  //只要队列不空,遍历就没有结束
            {
                int t =Q.front();  //取出对头元素
                Q.pop();
                printf(" %d ",t+1);  
                for(int j=0;j<G.vexnum;j++) //将其未访问过的邻接点加进入队列
                    if(G.arcs[t][j]==1&&visited[j]== false)
                    {
                        Q.push(j);
                        visited[j]=true; //在这里要设置true,因为这里该顶点我们已经加入到了队列,为了防止重复加入!
                    }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值