1 / 130

数据结构( Java 语言版)

数据结构( Java 语言版). —— 第 8 章 图. 第八阶段. 8. 图 【 知识要点 】 图的基本概念; 图的存储结构; 图的遍历; 生成树和最小生成树; 构造最小生成树的典型算法; 最短路径及其算法; 拓扑排序及其算法; AOE 网和关键路径。. 返回. 第八阶段. 8.1 实例引入 【 例 8.1】 北京及周边部分城市交通图。

wattan
Download Presentation

数据结构( Java 语言版)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 数据结构(Java语言版) ——第8章 图

  2. 第八阶段 8.图 • 【知识要点】 • 图的基本概念; • 图的存储结构; • 图的遍历; • 生成树和最小生成树; • 构造最小生成树的典型算法; • 最短路径及其算法; • 拓扑排序及其算法; • AOE网和关键路径。

  3. 返回 第八阶段 8.1 实例引入 • 【例8.1】北京及周边部分城市交通图。 如图8.1所示为北京及周边地区交通示意图,从图中可看出,北京市、天津市、承德市、唐山市、保定市、沧州市、廊坊市、张家口市相互之间都可以连通,选取其中五个城市及部分线路,抽象出如图8.2所示的交通路线图,其对应关系为北京市(A)、承德市(B)、唐山市(C)、保定市(D)、张家口市(E)。

  4. 第八阶段 图8.1 北京及周边部分城市交通示意图

  5. E B A C D 第八阶段 从图中可以看出,五个城市之间都有互相连通的道路,形成了一个多对多的关系,也称为图形关系,或者网状关系。 图8.2 北京周边地区交通模拟示意图

  6. 第八阶段 8.2 图的基本概念 • 8.2.1 图的定义 • 图(Graph)是一种网状数据结构,图是由结点(Vertices)集合V和边(Edges)集合E组成的。图中的结点又称为顶点。结点之间的关系称为边。图G的二元组定义如下: • G=(V,E) • 其中,V是结点的有限非空集合,E是边的有限集合。即: • V={u|u∈构成图的数据元素集合} • E={(u,v)|u,v∈V}或E={<u,v>| u,v∈V} • 其中,(u,v)表示结点u与结点v的一条无序偶,即(u,v)没有方向;而<u,v>表示从结点u到结点v的一条有序偶,即<u,v>是有方向的。

  7. 第八阶段 通常,图G的结点集合和边集合分别记为V(G)和E(G)。 E(G)可以是空集,此时图G只有结点没有边。 图的抽象数据类型定义如下: ADT Graph{ 数据对象V: V={vi|0≤i≤n-1,n≥0,vi∈某种数据结构} 数据关系E: E={(u,v)|u,v∈V}或E={<u,v>| u,v∈V}; 基本操作: getType() //返回当前图的类型 getVexNum() //返回图中结点数 getEdgeNum() //返回图中边数 getVertex() //返回图中所有结点的迭代器

  8. 第八阶段 • getEdge() //返回图中所有边的迭代器 • remove(v) //在图中删除特定的结点v • insert(e) //在图的边集中添加一条新边 • … • adjVertexs(u) //返回结点u的所有邻接点 • DFSTraverse(v) //从结点v开始深度优先搜索遍历图 • BFSTraverse(v) //从结点v开始广度优先搜索遍历图 • shortestPath(v) //求结点v到图中所有结点的最短路径 • generateMST() //求无向图的最小生成树有向图不支持此操作 • toplogicalSort() //求有向图的拓扑序列 • }ADT Graph • 对应于上述抽象数据类型,下面给出图的Java接口: • public interface Graph {

  9. 第八阶段 • 通常,图G的结点集合和边集合分别记为V(G)和E(G)。E(G)可以是空集,此时图G只有结点没有边。 • 图的抽象数据类型定义如下: • ADT Graph{ • 数据对象V: • V={vi|0≤i≤n-1,n≥0,vi∈某种数据结构} • 数据关系E: • E={(u,v)|u,v∈V}或E={<u,v>| u,v∈V}; • 基本操作: • getType() //返回当前图的类型 • getVexNum() //返回图中结点数 • getEdgeNum() //返回图中边数

  10. 第八阶段 • getVertex() //返回图中所有结点的迭代器 • getEdge() //返回图中所有边的迭代器 • remove(v) //在图中删除特定的结点v • insert(e) //在图的边集中添加一条新边 • … • adjVertexs(u) //返回结点u的所有邻接点 • DFSTraverse(v) //从结点v开始深度优先搜索遍历图 • BFSTraverse(v) //从结点v开始广度优先搜索遍历图 • shortestPath(v) //求结点v到图中所有结点的最短路径 • generateMST() //求无向图的最小生成树有向图不支持此操作 • toplogicalSort() //求有向图的拓扑序列 • }ADT Graph

  11. 第八阶段 • 对应于上述抽象数据类型,下面给出图的Java接口: • public interface Graph { • public static final int UndirectedGraph = 0; //无向图 • public static final int DirectedGraph = 1; //有向图 • public int getType(); //返回图的类型 • public int getVexNum(); //返回图的顶点数 • public int getEdgeNum(); //返回图的边数 • public Iterator getVertex(); //返回图的所有顶点 • public Iterator getEdge(); //返回图的所有边 • … • //返回从u出发可以直接到达的邻接顶点 • public Iterator adjVertexs(Vertex u);

  12. 第八阶段 • public Iterator DFSTraverse(Vertex v); • //对图进行深度优先遍历 • public Iterator BFSTraverse(Vertex v); • //对图进行广度优先遍历 • public Iterator shortestPath(Vertex v); • //求顶点v到其他顶点的最短路径 • //求无向图的最小生成树,如果是有向图不支持此操作 • public void generateMST() throws UnsupportedOperation; • //求有向图的拓扑序列 • public Iterator toplogicalSort() throws UnsupportedOperation; • }

  13. 第八阶段 • 其中UnsupportedOperation是调用图不支持的操作时抛出的异常,定义如下: • public class UnsupportedOperation extends RuntimeException { • public UnsupportedOperation(String err) { • super(err); • } • } • 8.2.2 图的相关概念 • 1.无向图(undirected graph) • 在一个图G中,如果两个结点之间构成的(u,v)∈E是无序偶,称该边是无向边。全部由无向边构成的图,称为无向图。 • 说明:用圆括号将一对结点括起来表示无向边,如(x,y)与(y,x)表示同一条边。如图8.3(a)所示,G1为无向图,G1的结点集合V和边集合E分别表示如下:

  14. (c) 有向图G3 (b) 有向图G2 2 1 3 4 2 1 3 4 (a) 无向图G1 第八阶段 • V(G1) = {1, 2, 3, 4} • E(G1) = {(1,2),(1,3),(2,3),(2,4),(3,4)}(a) 无向图G112431243(b) 有向图G2(c) 有向图G3 图8.3 图结构

  15. 第八阶段 • 2.有向图(directed graph) • 在图中,如果两个结点之间构成的<u,v>∈E是有序偶,称为该边为有向边,也称为弧(arc)。如<u,v>,u称为边的起点(initial node)或弧尾,v称为边的终点(terminal node)或弧头。全部由有向边组成的图,称为有向图。如图8.3(b)和(c)所示,G2和G3都是有向图。G2和G3的结点集合V和边集合E可分别表示为: • V(G2) = {1, 2, 3, 4} • E(G2) = {<1,2>,<1,3>,<1,4>,<2,4>,<3,4>} • V(G3) = {1, 2, 3} • E(G3) = {<1,2>,<2,1>,<2,3>,<3,3>} • 其中,G3中的<3,3>为自身环。 • 3.完全图(complete graph) • 具有n个结点的无向图G中,其边的最大数目为n×(n-1)/2,当边数为最大值时,则称图G为无向完全图。 • 具有n个结点的有向图G中,其边的最大数目为n×(n-1),当有向图G的边数为最大值时,则称图G为有向完全图。

  16. 第八阶段 • 4.稀疏图(sparse graph)和稠密图(dense graph) • 当一个图接近完全图时,则称该图为稠密图;相反地,当一个图含有较少的边数时,则称该图为稀疏图。 • 5.子图(subgraph) • 设有两个图G=(V,E)和G'=(V',E'),如果V'是V的子集,即V'V,而且E'是E的子集即E'E,则称G'为G的子图。即子图就是图G中的部分集合。例如,图8.3中无向图G1和有向图G2的部分子图,如图8.4所示。

  17. 第八阶段 2 2 1 1 1 1 3 3 4 3 2 2 1 1 1 1 3 3 4 4 (a) G1的部分子图 (b) G2的部分子图 图8.4 子图示例

  18. 第八阶段 • 如果G'为G的子图,且V'=V,称G'为G的生成子图(spanning subgraph),即V'=V,且E'E。 • 6.权(weight)和网(network) • 在一个图中,每条边可以标上具有某种含义的数值,此数值称为该边上的权,通常权是一个非负实数。权可以表示从一个结点到另一个结点的距离、花费的代价或时间等含义。边上标有权的图称为网,也称为带权图(weighted graph),如图8.5所示。

  19. 第八阶段 10 b a 2 5 f 8 6 c 12 e d 2 (a) 带权的无向图G4 (b) 带权的有向图G5 图8.5 带权图

  20. 第八阶段 • 7.邻接点(adjacent) • 在一个无向图中,若存在一条边(vi,vj),则称结点vi,vj互为邻接点。边(vi,vj)是结点vi和vj相关联的边,结点vi和vj是边(vi,vj)相关联的结点。 • 在一个有向图中,若存在一条边<vi,vj>,则称结点vi,vj互为邻接点。边(vi,vj)是结点vi和vj相关联的边,结点vi和vj是边<vi,vj>相关联的结点。 • 8.结点的度 • 结点的度(degree)是图中与结点v相关联边的数目,记为D(v)。例如,图8.3(a)所示G1中,结点1的度为2,记为D(v1)=2。度为1的结点称为悬挂点(pendant nodes)。 • 在有向图中,结点v的度有入度和出度之分,以v为终点的弧的数目称为入度(in degree),记为ID(v);以v为起点的弧的数目称为出度(out degree),记为OD(v)。出度为0的结点称为终端结点或叶子结点。结点的度等于它的入度和出度之和,即: • D(v)=ID(v)+OD(v)

  21. 第八阶段 • 例如,在图8.5(b)所示的G5中,结点5的入度ID(v5)=1,出度OD(v5)=2,度为D(v5)=3。 • 如果一个图中有n个结点和e条边,则该图所有顶点的度D(vi)与边数e满足如下关系: • 该式表示度与边的关系。每条边连接着两个结点,所以全部结点的度数为所有边数的2倍。 • 9.路径(path)与回路(cycle) • 在一个图G中,路径是从结点u(u∈V(G))到结点v(v∈V(G))所经过的结点序列,路径长度是指该路径上边的数目。如果一条路径上,序列中所有结点均不同,则称该路径为简单路径。如果一条路径上,起点和终点两个结点相同,则该路径被称为回路或环。除了第一个结点和最后一个结点相同,其余结点不重复出现的回路称为简单回路或简单环。 • 例如图8.6所示,从结点v1到结点v5的路径为<v1,v2>,<v2,v3>,<v3,v5>,缩写简记为{v1,v2,v3,v5},路径的长度为3,而且该路径属于简单路径。

  22. v2 v1 v6 v4 v3 v5 第八阶段 • {v1,v2,v4,v1,v2,v3,v5}不是简单路径,因为在这条路径中结点v1和结点v2重复出现。{v1,v2, v4,v1}就是一条简单回路,路径长度为3。 图8.6 有向图G6

  23. 第八阶段 • 另外,在带权图中,从起点到终点的路径上各条边上的权值之和,称为该路径长度。例如,图8.5(b)中带权图G5,从结点1到结点5的一条路径{v1,v2,v3,v5}的路径长度为2+9+8=19。 • 10.连通(connected)、连通图(connected graph)和连通分量(connected component) • 在无向图G中,如果从结点vi到结点vj有路径,则称vi与vj是连通的。若图G中,任意两个不同的结点都连通,则称为G为连通图,否则,称为非连通图。无向图G的极大连通子图,称为图G的连通分量。显然,任何连通图的连通分量只有一个,即本身。而非连通图可能有多个连通分量。如图8.5(a)所示,两个连通分量C1和C2。 • 11.强连通图(strongly connected graph)和强连通分量 • 在有向图G中,如果任意两个结点vi和vj,从结点vi到结点vj有路径,同时,从结点vj到结点vi也有路径,则称图G是强连通图。有向图G中的极大强连通子图称为图G的强连通分量。显然,强连通图只有一个强连通分量,即本身。非强连通图可能有多个强连通分量。如图8.7(b)所示,为强连通有向图。

  24. 第八阶段 1 v1 v2 n 2 v3 v2 v4 (C1) (C2) n-1 3 (a)两个连通分量 (b) 强连通有向图 图8.7 图的连通性

  25. 第八阶段 • 8.3 图的存储结构 • 8.3.1 邻接矩阵(adjacentcy matrix) • 图的邻接矩阵是表示结点之间相邻关系的矩阵。设图G=(V,E)具有n(n≥1)个结点,结点的顺序依次为{v0,v1,…,vn-1},则图G的邻接矩阵A是一个n阶方阵,定义如下: • 例如图8.8所示,无向图G7和有向图G8,对应的邻接矩阵分别为A1和A2如下所示:

  26. 第八阶段 v2 v2 v1 v1 v3 v3 v4 v4 (a) 无向图G7 (b) 有向图G8 图8.8 无向图G7和有向图G8

  27. 第八阶段 • 从邻接矩阵A1和A2不难看出,无向图的邻接矩阵是对称矩阵;有向图的邻接矩阵不一定对称。 • 存储邻接矩阵表示一个有n个结点的图,需要n2个存储单元。不带权的有向图的邻接矩阵一般来说是一个稀疏矩阵,当图的结点较多时,可以采用三元组表的方法存储邻接矩阵。 • 对于一个带权图G,设wij代表边(vi,vj)或<vi,vj>上的权值,则图G的邻接矩阵A定义如下:

  28. 第八阶段 • 例如,如图8.5所示,带权图G4和G5,对应的邻接矩阵分别为A3和A4如下所示。

  29. 第八阶段 • 用邻接矩阵表示图,很容易判断任意两个结点之间是否有边,并容易求出各个结点的度。对于无向图,邻接矩阵的第i行或第i列的非零元素正好是第i个结点vi的度;对于有向图,邻接矩阵的第i行的非零元素的个数正好是第i个结点vi的出度;第i列的非零元素的个数正好是第i个结点vi的入度。 • 对于一个具有n个结点的图G来说,可以将图G的邻接矩阵存储在一个二维数组matrix1中,声明一个MGraph1类,表示如下: • public class MGraph1{ //使用邻接矩阵存储图类 • protected int n; //图的结点个数 • protected int matrix1[][]; //利用二维数组存储图的邻接矩阵 • } • 图的邻接矩阵表示是惟一的。用邻接矩阵存储图,虽然能很好的确定图中的任意两个结点之间是否有边,但是,要确定图中有多少条边,则必须按行、按列对每个数据元素进行检测,花费的时间代价较大。不论是求任意一个结点的度,还是查找任意一结点的邻接点,都需要访问对应的一行或一列中的所有元素,其时间复杂度为O(n),n为邻接矩阵的阶数。对于结点为n的图来说,从空间上看,不论图中的结点之间是否有边,都要在邻接矩阵中预留存储空间,其空间复杂度为O(n2)。空间效率较低。这也是邻接矩阵存储图的局限性。

  30. 第八阶段 • 8.3.2 邻接表(adjacentcy list) • 邻接表是图的一种链式存储方法,邻接表表示类似于树的孩子链表表示。在邻接表中,对于图G中的每个结点vi建立一个单链表,第i个单链表中的结点表示依附于结点vi的边(对于有向图就是以结点vi为尾的弧),即将所有邻接于结点vi的结点链成一个单链表,并在表头附设一个表头结点,这个单链表就称为结点vi的邻接表。 • 邻接表包括两部分:表头结点和表结点,其结点结构如下:

  31. 第八阶段 表头结点 表头结点 表结点 表结点 data data firstarc firstarc adjvex adjvex nextarc nextarc info info

  32. 第八阶段 • 表头结点包括:data和firstarc两个成员。data表示结点数据元素的信息;firstarc表示指向链表中的第1个结点。 • 表结点包括:adjvex、nextarc和info三个成员。adjvex指示于结点vi邻接的点在图中的位置;nextarc指示下一条边或弧的结点;info存储与边相关的信息,如权值等。图中的每个结点用表结点表示,表结点中都对应于该结点相关联的一条边。 • 例如,图8.8中的无向图G7、有向图G8和图8.5中的带权有向图G5对应的邻接表分别如图8.9(a)、(b)和(c)所示:

  33. 第八阶段 0 0 v1 1 3 0 3 3 0 3 2 1 2 v1 1 2 3 0 v1 0 ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ 1 1 v2 v2 2 2 v3 v3 3 3 v4 v4 (a)无向图G7对应的邻接表 0 1 2 3 2 ∧ 1 v2 0 4 2 9 ∧ 2 v3 4 8 ∧ 3 v4 2 7 ∧ 4 v5 2 5 3 1 ∧ (c)带权有向图G5对应的邻接表 (b)无向图G8对应的邻接表 图8.9 三个邻接表

  34. 第八阶段 • 邻接表具有以下特点: • (1)邻接表的表示不是惟一的。因为在每个结点的邻接表中,各边结点的链接次序可以随意安排,取决与建立邻接表的算法及与边的输入次序。 • (2)在无向图的邻接表中,结点vi的度恰为该结点的邻接表中边结点的个数;而在有向图中,结点vi的邻接表中边结点的个数仅为该结点的出度。有向图中结点的入度,可以通过建立一个有向图的逆邻接表得出。 • (3)对于n个结点和e条边的无向图,其邻接表有n个结点和2e个边结点。显然,在边数小于n×(n-1)/2时,邻接表比邻接矩阵节省存储空间。

  35. 第八阶段 • 8.4 图的遍历 • .4.1 深度优先搜索遍历(DFS) • 深度优先搜索(depth first search)遍历类似于树的先根遍历,是树先根遍历的推广。 • 1.算法描述 • 从图中的某个结点v开始访问,访问它的任意一个邻接结点w1;再从w1出发,访问与w1邻接但还没有被访问过的结点w2;然后再从w2出发,进行类似的访问,……如此进行下去,直至所有的邻接结点都被访问过为止。接着,退回一步,退到前一次刚访问过的结点,看是否还有其它没有被访问的邻接结点。如果有,则访问此结点,之后再从此结点出发,进行与前述类似的访问。重复上述过程,直到连通图中所有结点都被访问过为止。 • 遍历的过程是一个递归的过程。 • 例如,图8.10(a)无向图G9进行深度优先搜索遍历的过程,如图8.10(b)所示。

  36. 第八阶段 v1 v1 v2 v3 v2 v3 v4 v5 v6 v7 v4 v5 v6 v7 v8 v8 (b) 图G9的深度优先搜索遍历过程 (a) 无向图G9 图8.10 深度优先搜索遍历的过程

  37. 第八阶段 • 假定v1是出发点,首先访问v1。因v1有两个邻接点v2、v3均未被访问过,选择访问结点v2,再找v2的未被访问过的邻接点v4、v5,选择访问结点v4。重复上述搜索过程,依次访问结点v8、v5。v5被访问过后,由于与v5相邻的顶点均已被访问过,搜索退回到v8。v8的邻接点v4、v5也被访问过;同理,依次退回结点v4、v2,最后退回到结点v1。这时选择结点v1的未被访问过的邻接点v3,继续搜索,依次访问结点v3、v6、v7,从而遍历图中全部结点。这就是深度优先搜索遍历的整个过程,得到的结点的深度遍历序列为: • {v1,v2,v4,v8,v5,v3,v6,v7} • 图的深度优先搜索遍历的过程是递归的。深度优先搜索遍历图所得的结点序列,定义为图的深度优先遍历序列,简称DFS序列。一个图的DFS序列不一定是惟一的。

  38. 第八阶段 • 2.图的深度优先搜索算法实现 • 从某个结点v出发的深度优先搜索过程是一个递归的搜索过程,因此可简单的使用递归算法实现。在遍历的过程中,必须对访问过的结点做标记,避免同一结点被多次访问。深度优先搜索算法的具体实现如下: • 算法8.1:图的非递归深度优先搜索算法和递归深度优先搜索算法 • //对图进行深度优先遍历 • public Iterator DFSTraverse(Vertex v) { • LinkedList traverseSeq = new LinkedListDLNode(); • resetVexStatus(); //重置结点状态 • DFS(v, traverseSeq); //从v点出发深度优先搜索 • Iterator it = getVertex(); //从图中未曾访问的其他结点出发重新搜索

  39. 第八阶段 • for(it.first(); !it.isDone(); it.next()){ • Vertex u = (Vertex)it.currentItem(); • if (!u.isVisited()) DFS(u, traverseSeq); • } • return traverseSeq.elements(); • } • //深度优先的递归算法 • private void DFSRecursion(Vertex v, LinkedList list){ • v.setToVisited(); • list.insertLast(v); • Iterator it = adjVertexs(v); //取得结点v的所有邻接点 • for(it.first(); !it.isDone(); it.next()){ • Vertex u = (Vertex)it.currentItem(); • if (!u.isVisited()) DFSRecursion(u,list); • } • }

  40. 第八阶段 • 在算法中对图进行深度优先搜索遍历时,对图中每个结点最多调用一次DFSRecursion方法,因为某个结点一旦被访问,就不再从该结点出发进行搜索。因此,遍历图的过程实际就是查找每个结点的邻接点的过程。设图G有n个结点和e条边(e≥n),当存储结构采用邻接矩阵存储时,需要扫描邻接矩阵中的每一个结点,其时间复杂度为O(n);当存储结构采用邻接表时,需要扫描邻接表中的每个边结点,所以其时间复杂度为O(e);两者的空间复杂度都为O (n)。 • 【例8.2】无向图的深度优先搜索遍历示例。 • 分析:根据深度优先搜索遍历的算法,从某个开始访问,可以用如下步骤实现: • (1)对于某个结点如果可能,则访问该结点未被访问的其中一个邻结点,输出该结点,并把该结点放入栈中,给于标记。 • (2)当(1)不能执行而且栈不为空时,从栈中弹出一个结点。 • (3)如果(1)和(2)都不能执行,就完成了图的深度优先搜索遍历。

  41. 第八阶段 • 参考程序如下: • import java.util.Stack; • class Vertex{ • public char label; • public boolean wasVisited; • public Vertex(char lab){ • label = lab; • wasVisited = false; • } • } • class Graph{ • private final int MAX_VERTS = 20; • private Vertex vertexList[]; //邻接结点 • private int adjMat[][]; //邻接矩阵 • private int nVerts; //结点数 • private Stack<Integer> theStack;

  42. 第八阶段 • public Graph(){ • vertexList = new Vertex[MAX_VERTS]; • adjMat = new int[MAX_VERTS][MAX_VERTS]; //邻接矩阵 • nVerts = 0; • for(int y=0; y<MAX_VERTS; y++) • for(int x=0; x<MAX_VERTS; x++) • adjMat[x][y] = 0; • theStack = new Stack<Integer>(); • } • public void addVertex(char lab) { • vertexList[nVerts++] = new Vertex(lab); • } • public void addEdge(int start, int end) {

  43. 第八阶段 • adjMat[start][end] = 1; • adjMat[end][start] = 1; • } • public void displayVertex(int v){ • System.out.print(vertexList[v].label); • } • public void DFS(){ //深度优先搜索 • vertexList[0].wasVisited = true; //标记 • displayVertex(0); //输出 • theStack.push(0); //压栈 • while( !theStack.isEmpty() ) { //栈不为空 • int v = getAdjUnvisitedVertex(theStack.peek()); • if(v == -1) • theStack.pop(); • else{ • vertexList[v].wasVisited = true; • displayVertex(v); • theStack.push(v); • } • } // 栈为空,则搜索完毕

  44. 第八阶段 • for(int j=0; j<nVerts; j++) • vertexList[j].wasVisited = false; • } • public int getAdjUnvisitedVertex(int v){ • for(int j=0; j<nVerts; j++) • if(adjMat[v][j]==1 && vertexList[j].wasVisited==false) • return j; • return -1; • } • } • class DFSApp { • public static void main(String[] args){ • Graph theGraph = new Graph(); • theGraph.addVertex('A'); // 0

  45. 第八阶段 • theGraph.addVertex('B'); // 1 • theGraph.addVertex('C'); // 2 • theGraph.addVertex('D'); // 3 • theGraph.addVertex('E'); // 4 • theGraph.addEdge(0, 1); // AB • theGraph.addEdge(1, 2); // BC • theGraph.addEdge(0, 3); // AD • theGraph.addEdge(3, 4); // DE • System.out.print("遍历的结果: "); • theGraph.DFS(); • System.out.println(); • } • } • 程序运行的结果为: • 遍历的结果: ABCDE

  46. 第八阶段 • 8.4.2 广度优先搜索遍历(BFS) • 广度优先搜索(breadth first search)遍历类似于树的层次遍历,是树的层次遍历的推广。 • 从图中的某个结点v开始访问,依次访问结点v的各个未被访问过的邻接结点w1,w2,……然后依次顺序访问结点w1,w2,……的各个还未被访问过的邻接结点。再从这些访问过的结点出发,依次访问它们的所有还未被访问过的邻接结点,……重复上述过程,直到图中所有结点都被访问过为止。也就是说:广度优先搜索遍历图的过程是一个以v为起始点,由近及远,依次访问和v有路径相通且路径长度为1,2,3……的结点,且遵循先被访问的结点,其邻接结点就先被访问。

  47. 第八阶段 • 广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批结点,不像深度优先搜索那样有回退的情况。因此,广度优先搜索不是一个递归的过程。 • 在广度优先搜索遍历中,需要使用队列,依次记住被访问过的结点。 • 算法开始时,访问初始结点v后,并插入队列中,以后每从队列中删除一个元素,就依次访问它的每一个未被访问过的邻接结点,并令其进入队列。这样,当队列为空时,表明所有与起点相通的结点都已被访问完毕,算法结束。 • 例如,图8.10(a)无向图G9从v1出发进行广度优先搜索遍历的过程。 • 首先,访问起点v1。v1有两个未曾访问的邻接结点v2和v3。先访问v2,再访问v3。然后,再访问v2的未曾访问过的邻接结点v4、v5及v3的未曾访问过的邻接结点v6和v7,最后访问v4的未曾访问过的邻接结点v8。至此图中所有顶点均已被访问过。得到的顶点访问序列为: • {v1,v2,v3,v4,v5,v6,v7,v8}

  48. v1 第八阶段 • 其队列操作过程如下: • (1)首先,从结点v1开始广度优先搜索,将结点v1存入队列中。

  49. v2 v3 第八阶段 • (2)将结点v1从队列中取出,将结点v1的邻接结点v2和v3依次存入队列中。

  50. v3 v4 v5 第八阶段 • (3)将结点v2从队列中取出,然后,按照先访问v2的未曾被访问过的邻接点,再访问v3未曾被访问过的邻接点的次序。即取出结点v2,将其邻接结点v4和v5依次存入队列中。

More Related