图的遍历方法和图的几个典型算法

本文介绍了图的遍历方法,包括深度优先搜索(DFS)和广度优先搜索(BFS),并分析了两种算法的时间复杂度。此外,讨论了图的应用,如最小生成树的构造(普里姆和克鲁斯卡尔算法)、最短路径问题(Dijkstra和Floyd算法),以及拓扑排序和关键路径计算。这些概念和算法在解决网络问题和优化任务中起着重要作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图的遍历方法和图的几个典型算法

图的遍历

  • 遍历定义——从已给的连通图中某一顶点出发,沿着一边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。

  • 遍历实质——找每个顶点的邻接点的过程。

  • 图的特点——图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回了曾经访问过的顶点。


  • 怎样避免重复
    解决思路——设置辅助数组 visited[n]visited[n]visited[n],用来标记没个被访问过的顶点。
    • 初始状态visited[i]为0visited[i]为0visited[i]0
    • 顶点iii被访问,改visited[i]visited[i]visited[i]为1,防止被多次访问。

  • 图常用的遍历
    • 深度优先搜索(Depth_First Search——DFS)
    • 广度优先搜索(Breadth_First Search——BFS)

深度优先搜索

深搜例子:

在这里插入图片描述

  • 方法:
    • 在访问图中某一起始顶点vvv后,由以vvv出发访问它的任一邻接顶点w1w_1w1
    • 再从w1w_1w1出发,访问与w1w_1w1邻接但还未被访问过的顶点w2w_2w2
    • 再从w2w_2w2出发,进行类似的访问, …
    • 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点uuu为止。
    • 接着,退回一步,退到前一次刚访问过的顶点看是否还有其它没有被访问的邻接顶点。
    • 如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;
    • 如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。

例子:

在这里插入图片描述

  • 邻接矩阵表示的无向图深度遍历实现:

在这里插入图片描述

  • 采用邻接矩阵表示图的深度优先搜索遍历——算法实现
void DFS(AMGraph G,int v){  //图G为邻接矩阵类型
    cout<<v; visited[v]=true;  //访问第v个顶点
    for(int w=0;w<G.vexnum;w++)  //依次检查邻接矩阵v所在的行
        if(G.arc[v][w]!=0&&(!visited[w])) 
            DFS(G,w);
    // w是v的邻接点,如果w为访问,则递归调用DFS
}

  • DFS算法效率分析
    • 用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描所在行,时间复杂度为O(n2)O(n^2)O(n2)
    • 用邻接表来表示图,虽然有2e2e2e个表结点,但只需扫描eee个结点即可完成遍历,加上访问nnn个头结点的时间,时间复杂度为O(n+e)O(n+e)O(n+e)
    • 结论:
      • 稠密图适于在邻接矩阵上进行深度遍历
      • 稀疏图适于在邻接表上进行深度遍历

广度优先搜索

广搜例子:

在这里插入图片描述

  • 方法:
    • 从图的某一结点出发,首先依次访问该结点的所有邻接点Vi1,Vi2,...,VnV_i{_1},V_i{_2},...,V_nVi1,Vi2,...,Vn再按这些顶点被访问的先后次序依次访问与它们相邻接的所有未被访问的顶点
    • 重复此过程,直至所有顶点均被访问为止。

例:

在这里插入图片描述


// 按广度优先非递归遍历连通图
void BFS(Graph G int v){
  cout<<v;  visited[v]=true; //访问第v个顶点
  InitQueue(Q);  //辅助队列Q初始化,置空
  EnQueue(Q,v); //v进队
  while(!QueueEmpty(Q)) {  //队列非空
    DeQueue(Q,u);  //对头元素出对并置为u
    for(int w=FirstAdjVex(G,u);w>0;w=NextAdjVex(G,u,w))
      if(!visited[w]){  // w为u的尚未访问的邻接顶点
        cout<<w; visited[w]=true; EnQueue(Q,w); //w进队
      } // if
  }  // while
}  // BFS
  • BFS算法效率分析
    • 如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行(nnn个元素),总的时间代价O(n2)O(n^2)O(n2)
    • 用邻接表来表示图,虽然有2e2e2e个表结点,但只需扫描eee个结点即可完成遍历,加上访问nnn个头结点的时间,时间复杂度为O(n+e)O(n+e)O(n+e)

DFS和BFS算法效率比较

  • 空间复杂度相同,都是O(n)O(n)O(n)(借用了堆栈或队列)
  • 时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。

图的应用

最小生成树

  • 生成树——所有顶点均由边连接在一起,但不存在回路的图
    • 一个图可以有许多生成树
    • 所有生成树具有以下共同特点
      • 生成树的顶点个数与图的顶点个数相同;
      • 生成树是图的极小连通子图,去掉一条边则非连通;
      • 一个有n个顶点的连通图的生成树有n-1条边;
      • 在生成树中再加一条边必然形成回路;
      • 生成树中任意两个顶点间的路径是唯一的。
    • 含n个顶点n-1条边的图不一定是生成树。

例:
在这里插入图片描述


  • 无向图的生成树

在这里插入图片描述


  • 最小生成树定义——给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树,也叫最小代价生成树。

最小生成树的典型应用

在这里插入图片描述

  • 构造最小生成树——MST性质

构造最小生成树的算法很多,其中多数算法都利用了MST的性质。

MST性质:设N=(V,E)N=(V,E)N=(V,E)是一个连通网,UUU顶点集VVV的一个非空子集。若边(u,v)(u,v)(u,v)是一条具有最小权值的边,其中u∈U,v∈V−Uu∈U,v∈V-UuU,vVU,则必存在一棵包含边(u,v)(u,v)(u,v)的最小生成树。


  • MST性质解释:
    在生成树的构造过程中,图中n个顶点分属两个集合:

    • 已落在生成树上的顶点集;
    • 尚未落在生成树上的顶点集

    接下来则应在所有连通U中顶点的边中选取权值最小的边。

图示:

在这里插入图片描述

构造最小生成树方法一:普里姆(Prime)算法
  • 算法思想——加结点法
    • N=(V,E)N=(V,E)N=(V,E)是连通网,TETETENNN上最小生成树中边的集合。
    • 初始化U=u0,(u0∈V),TE=U={u_0},(u_0∈V),TE={}U=u0,(u0V),TE=
    • 在所有u∈U,v∈V−Uu∈U,v∈V-UuU,vVU的边(u,v)∈E(u,v)∈E(u,v)E中,找一条代价最小的边(u0,v0)(u_0,v_0)(u0,v0)
    • (u0,v0)(u_0,v_0)(u0,v0)并入集合TETETE,同时v0v_0v0并入UUU
    • 重复上述操作直至U=VU=VU=V为止,则T=(V,TE)T=(V,TE)T=(V,TE)NNN的最小生成树。

例子:

在这里插入图片描述


构造最小生成树方法二:克鲁斯卡尔(Kruskal)算法
  • 算法思想——加边法(贪心)
    • 设连通网N=(v,E)N=(v,E)N=(v,E),令最小生成树初始状态为只有nnn个顶点而无边的非连通图T=(V,)T=(V,{})T=(V,),每个顶点自成一个连通分量。
    • EEE中选取代价最小的边,若该边依附的顶点落在TTT中不同的连通分量上(即:不能形成环),则将此边加入到TTT中;否则,舍去此边,选取下一条代价最小的边。
    • 依次类推,直至TTT中所有顶点都在同一连通分量上为止。

例子:

在这里插入图片描述

  • 最小生成树不一定唯一
两种算法比较
算法名普里姆算法克鲁斯卡尔算法
算法思想选择点选择边
时间复杂度O(n2)O(n^2)O(n2)(nnn为顶点数)O(eloge)O(eloge)O(eloge)(eee为边数)
适应范围稠密图稀疏图

最短路径

  • 典型用途:交通网络问题——从甲地到乙地之间是否有公路相连?在有多条通路的情况下,哪一条路最短?
    交通网络用有向网来表示:
    • 顶点——表示地点
    • 弧——表示两个地点有路连通
    • 弧上的权值——表示两地点之间的距离、交通费或途中所花费的时间等。

如何能够使一个地点到另一个地点的运输时间最短或运费最省?这就是一个求两个地点间的最短路径问题。

  • 问题抽象
    在有向网中A点(源点)到达B点(终点)的多条路径中,寻找一条各边权值之和最小的路径,即最短路径。

最短路径与最小生成树不同,路径上不一定包含n个顶点,也不一定包含n-1条边。

  • 第一类问题:两点间最短路径——Dijkstra(迪杰斯特拉)算法

在这里插入图片描述

  • 第二类问题:某源点到其他各点最短路径——Floyd(弗洛伊德)算法

在这里插入图片描述

求单源最短路径——Dijkstra(迪杰斯特拉)算法
  • 算法思想:

1.初始化:先找到从源点v0v_0v0到各终点vkv_kvk的直达路径(v0,vk)(v_0,v_k)(v0,vk),即通过一条弧到达的路径。
2.选择:从这些路径中找出一条长度最短的路径(v0,u)(v_0,u)(v0,u)
3.更新:然后对其余各条路径进行适当调整。
  若在图中存在弧(u,vk)(u,v_k)(u,vk),且(v0,u)+(u,vk)<(v0,vk)(v_0,u)+(u,v_k)<(v_0,v_k)(v0,u)+(u,vk)<(v0,vk),则以路径(v0,u,vk)(v_0,u,v_k)(v0,u,vk)代替(v0,vk)(v_0,v_k)(v0,vk)

在调整后的各条路径中,在找长度最短的路径,以此类推。

  • 迪杰斯特拉(Dijkstra)算法:按路径长度递增次序产生最短路径

  • 1、把V分成两组:

    • (1)SSS:已求出最短路径的顶点的集合。
    • (2)T=V−ST=V-ST=VS:尚未确定最短路径的顶点集合。
  • 2、将TTT中顶点按最短路径递增的次序加入到SSS
    保证:

    • (1)从源点v0v_0v0SSS中各顶点的最短路径长度都不大于从v0v_0v0TTT中任何顶点的最短路径长度。
    • (2)每个顶点对应一个距离值:
      • S中顶点:从v0v_0v0到此顶点的最短路径长度。
      • T中顶点:从v0v_0v0到此顶点的只包括SSS中顶点作中间顶点的最短路径长度。

例子:

在这里插入图片描述

  • 时间复杂度:O(n3)O(n^3)O(n3)
求多源最短路径——Floyd(弗洛伊德)算法
  • 求所有顶点间的最短路径:
    方法一:每次以一个顶点为源点,重复执行Dijkstra算法n次——时间复杂度:O(n3)O(n^3)O(n3)
    方法二:弗洛伊德(Floyd)算法——时间复杂度:O(n3)O(n^3)O(n3)

  • 算法思想:

    • 逐个顶点试探
    • viv_ivivjv_jvj的所有可能存在的路径中
    • 选出一条长度最短的路径

例子:

在这里插入图片描述

拓扑排序

针对特殊的图——有向无环图

  • 有向无环图——无环的有向图,简称:DAG图(Directed Acycline Graph)

在这里插入图片描述


  • AOV网:
    用一个有向图表示一个工程的各子工程及其相互制约的关系,其中以顶点表示活动,弧表示活动之间的制约关系,称这种有向图为顶点表示活动的网,简称AOV网(Activity On Vertex)。

  • AOE网:
    用一个有向网表示一个工程的各子工程及其相互制约的关系,以弧表示活动,以顶点表示活动的开始或结束事件,称这种有向图为边表示活动的网,简称AOE网(Activity On Edge)


  • AOV网的特点:
    • 若从iiijjj有一条有向路径,则iiijjj的前驱;jjjiii的后继。
    • <i,j><i,j><i,j>是网中的有向边,则iiijjj的直接前驱,jjjiii的直接后继。
    • AOV网中不允许有回路,因为如果有回路存在,则表明某项活动以自己为先决条件,显然这是荒谬的。
    • 问题——如何判别AOV网中是否存在回路?

  • 拓扑排序定义
    在AOV网没有回路的前提下,我们将全部活动排列成一个线性序列,使得若AOV网中有弧<i,j><i,j><i,j>存在,则在这个序列中,iii一定排在jjj的前面,具有这样的性质的线性序列成为拓扑有序序列,相应的拓扑有序排序的算法成为拓扑排序。

  • 拓扑排序的方法
    • 在有向图中选一个没有前驱的顶点且输出之。
    • 从图中删除该顶点和所有以它为尾的弧。
    • 重复上述两步,直至全部顶点均已输出或者当图中不存在无前驱的顶点为止。

例子:

在这里插入图片描述

  • 拓扑排序的应用——检测AOV网中是否存在环
    • 方法:对有向图构造器顶点的拓扑有序序列,若网中所有顶点都在它的拓扑有序序列中,则该AOV网必定不存在环。

关键路径

问题例子:

在这里插入图片描述

在这里插入图片描述


  • 把工程计划表示为边表示活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权值表示活动持续时间。

  • 事件表示在它之前的活动已经完成,在它之后的活动可以开始。

例子:
在这里插入图片描述

  • 对于AOE网,我们关心两个问题:——求解关键路径问题

    • (1)完成整项工程至少需要多少时间?
    • (2)哪些活动是影响工程进度的关键?
  • 关键路径——路径长度最长的路径

  • 路径长度——路径上各活动持续时间之和。


在这里插入图片描述

  • 如何确定关键路径,需要定义4个描述量:

    • ve(vj)ve(v_j)ve(vj)——表示事件vjv_jvj的最早发生时间。eg:ve(v1)=0,ve(v2)=30ve(v_1)=0,ve(v_2)=30ve(v1)=0,ve(v2)=30
    • vl(vj)vl(v_j)vl(vj)——表示事件vjv_jvj的最迟发生时间。eg:vl(v4)=165vl(v_4)=165vl(v4)=165
    • e(i)e(i)e(i)——表示活动aia_iai的最早开始时间。eg:e(a3)=30e(a_3)=30e(a3)=30
    • l(i)l(i)l(i)——表示活动aia_iai的最迟开始时间。eg:l(a3)=120l(a_3)=120l(a3)=120

    l(i)−e(i)l(i)-e(i)l(i)e(i)——表示完成活动a(i)a(i)a(i)的时间余量。eg:l(3)−e(3)=90l(3)-e(3)=90l(3)e(3)=90
    关键活动——关键路径上的活动,即l(i)==e(i)l(i)==e(i)l(i)==e(i)。(即l(i)−e(i)==0l(i)-e(i)==0l(i)e(i)==0)的活动。

例:

在这里插入图片描述

  • 关键路径的求解步骤

在这里插入图片描述
关键路径:l−e==0l-e==0le==0的点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galactus_hao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值