141
图图图图图图 图图图图图图 图图图图图图图图 图图图图图 图图图图 图图图图 图图

图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

  • Upload
    nelly

  • View
    184

  • Download
    7

Embed Size (px)

DESCRIPTION

第八章 图. 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结. 图的基本概念. 图定义 图是由顶点集合 (vertex) 及顶点间的关系集合组成的一种数据结构 : Graph = ( V , E ) 其中 V = { x | x  某个数据对象 } 是顶点的有穷非空集合; E = {( x , y ) | x , y  V } 或 E = {< x , y> | x , y  V && Path ( x , y )} - PowerPoint PPT Presentation

Citation preview

Page 1: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

Page 2: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的基本概念图的基本概念 图定义图定义 图是由顶点集合图是由顶点集合 (vertex) 及顶点间的及顶点间的关系集合组成的一种数据结构关系集合组成的一种数据结构: GraphGraph == ( ( VV, , E E )) 其中 其中 VV = { = { xx | | x x 某个数据对象某个数据对象 }} 是顶点的有穷非空集合;是顶点的有穷非空集合; EE = {( = {(xx, , yy) |) | x x, , y y V V }} 或或 EE = {< = {<xx, , y>y> | | x x, , y y VV && && PathPath ( (xx, , yy)})} 是顶点之间关系的有穷集合,也叫做边是顶点之间关系的有穷集合,也叫做边 (edg(edg

e)e) 集合。集合。 PathPath ( (xx, , yy)) 表示从 表示从 x x 到 到 y y 的一条单的一条单向通路向通路 , , 它是有方向的。它是有方向的。

Page 3: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

有向图与无向图 有向图与无向图 在有向图中,顶点对在有向图中,顶点对 <x, y><x, y> 是是有序的。在无向图中,顶点对有序的。在无向图中,顶点对 (x, y)(x, y) 是无序的。是无序的。 完全图 完全图 若有 若有 n n 个顶点的无向图有 个顶点的无向图有 nn((nn-1)/2 -1)/2 条边条边 , , 则此图为完全无向图。有 则此图为完全无向图。有 n n 个顶点的有向个顶点的有向图有图有 nn((n-n-1) 1) 条边条边 , , 则此图为完全有向图。则此图为完全有向图。

邻接顶点 邻接顶点 如果 如果 ((uu, , vv) ) 是 是 EE(G) (G) 中的一条边,中的一条边,则称 则称 u u 与 与 v v 互为邻接顶点互为邻接顶点。

Page 4: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

权 权 某些图的边具有与它相关的数某些图的边具有与它相关的数 , , 称之为权。这称之为权。这种带权图叫做网络。种带权图叫做网络。 子图 子图 设有两个图 设有两个图 GG == ((VV, , EE) ) 和 和 G‘G‘ == ((VV’, ’, EE‘)‘) 。。若 若 VV’’ V V 且 且 EE‘‘EE, , 则称 图则称 图 G’ G’ 是 图是 图 G G 的子图。的子图。

顶点的度 顶点的度 一个顶点一个顶点 vv 的度是与它相关联的边的条的度是与它相关联的边的条数。记作数。记作 TD(TD(vv)) 。。在有向图中在有向图中 , , 顶点的度等于该顶顶点的度等于该顶点的入度与出度之和。点的入度与出度之和。

Page 5: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

顶点 顶点 v v 的入度的入度是以 是以 v v 为终点的有向边的条数为终点的有向边的条数 , , 记作 记作 ID(ID(vv)); ; 顶点 顶点 vv 的出度的出度是以 是以 v v 为始点的有为始点的有向边的条数向边的条数 , , 记作 记作 OD(OD(vv)) 。。

路径 路径 在图 在图 GG == ((VV, , EE) ) 中中 , , 若从顶点若从顶点 vvi i 出发出发 ,, 沿一些边经过一些顶点沿一些边经过一些顶点 vvpp11, , vvpp22, …, , …, vvpmpm ,到达顶,到达顶点点 vvjj 。则称顶点序列 。则称顶点序列 ( ( vvi i vvpp1 1 vvpp2 2 ... ... vvpm pm vvj j )) 为从顶点为从顶点vvi i 到顶点 到顶点 vvj j 的路径的路径。它经过的边。它经过的边 ((vvii, , vvpp11)) 、、 ((vvpp11, , vvpp

22)) 、、 ...... 、、 ((vvpmpm,, v vjj)) 应是属于应是属于 EE 的边。的边。 路径长度 路径长度

非带权图的路径长度是指此路径上边的条数。非带权图的路径长度是指此路径上边的条数。 带权图的路径长度是指路径上各边的权之和。带权图的路径长度是指路径上各边的权之和。

Page 6: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

简单路径 简单路径 若路径上各顶点 若路径上各顶点 vv11,,vv22,...,,...,vvm m 均不互相均不互相重复重复 , , 则称这样的路径为简单路径。则称这样的路径为简单路径。 回路 回路 若路径上第一个顶点 若路径上第一个顶点 vv1 1 与最后一个顶点与最后一个顶点 vv

m m 重合重合 , , 则称这样的路径为回路或环。则称这样的路径为回路或环。

连通图与连通分量 连通图与连通分量 在无向图中在无向图中 , , 若从顶点若从顶点 vv11 到顶到顶点点 vv22 有路径有路径 , , 则称顶点则称顶点 vv11 与与 vv22 是连通的。如果图中是连通的。如果图中任意一对顶点都是连通的任意一对顶点都是连通的 , , 则称此图是连通图。非则称此图是连通图。非连通图的极大连通子图叫做连通分量。连通图的极大连通子图叫做连通分量。

Page 7: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

强连通图与强连通分量 强连通图与强连通分量 在有向图中在有向图中 , , 若对于若对于每一对顶点每一对顶点 vvii 和和 vvjj, , 都存在一条从都存在一条从 vvii 到到 vvjj 和从和从 vvjj到到 vvii 的路径的路径 , , 则称此图是强连通图。非强连通图则称此图是强连通图。非强连通图的极大强连通子图叫做强连通分量。的极大强连通子图叫做强连通分量。 生成树 生成树 一个连通图的生成树是它的极小连通一个连通图的生成树是它的极小连通子图,在子图,在 nn 个顶点的情形下,有个顶点的情形下,有 nn-1-1 条边。但有条边。但有向图则可能得到它的由若干有向树组成的生成森向图则可能得到它的由若干有向树组成的生成森林。林。 本章不予本章不予 讨论的图讨论的图

Page 8: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的抽象数据类型图的抽象数据类型class Graph {

public: Graph ( );

void InsertVertex ( Type & vertex ); void InsertEdge

( int v1, int v2, int weight ); void RemoveVertex ( int v ); void RemoveEdge ( int v1, int v2 ); int IsEmpty ( ); Type GetWeight ( int v1, int v2 ); int GetFirstNeighbor ( int v ); int GetNextNeighbor ( int v1, int v2 ); }

Page 9: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的存储表示图的存储表示 在图的邻接矩阵表示中,有一个在图的邻接矩阵表示中,有一个记录各个顶点信记录各个顶点信息息的的顶点表顶点表,还有一个,还有一个表示各个顶点之间关系表示各个顶点之间关系的的邻接矩阵邻接矩阵。。 设图 设图 A = (A = (VV, , EE)) 是一个有 是一个有 n n 个顶点的图,则图个顶点的图,则图的邻接矩阵是一个二维数组 的邻接矩阵是一个二维数组 AA.edge.edge[[nn][][nn]] ,定义:,定义:

无向图的邻接矩阵是对称的,有向图的邻接矩阵无向图的邻接矩阵是对称的,有向图的邻接矩阵可能是不对称的。可能是不对称的。

邻接矩阵 邻接矩阵 (Adjacency Matrix)(Adjacency Matrix)

,

),( , ,]][[ .

否则或者如果

0><1

AEjiEji

jiEdge

Page 10: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在有向图中在有向图中 , , 统计第 统计第 ii 行 行 1 1 的个数可得顶点 的个数可得顶点 ii 的出度,的出度,统计第 统计第 j j 行 行 1 1 的个数可得顶点 的个数可得顶点 jj 的入度。的入度。 在无向图中在无向图中 , , 统计第 统计第 ii 行 行 (( 列列 ) 1 ) 1 的个数可得顶点的个数可得顶点 ii 的度。的度。

Page 11: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

网络的邻接矩阵网络的邻接矩阵

),( ,

),(

]][[ .ji

jiEjiEjijijiW

jiEdge==

=! ,<!

0=A

对角线但是否则

或且如

Page 12: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

用邻接矩阵表示的图的类的定义用邻接矩阵表示的图的类的定义const int MaxEdges = 50;const int MaxVertices = 10;

template <class NameType, class DistType>class Graph {private: SeqList<NameType> VerticesList (MaxVertices); DistType Edge[MaxVertices][MaxVertices]; int CurrentEdges; int FindVertex ( SeqList<NameType> & L; const NameType &vertex ) { return L.Find (vertex); }

Page 13: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

int GetVertexPos ( Const NameType &vertex ) { return FindVertex (VerticesList, vertex ); }public: Graph ( int sz = MaxNumEdges ); int GraphEmpty ( )

const { return VerticesList.IsEmpty ( ); } int GraphFull( ) const { return VerticesList.IsFull( ) || CurrentEdges ==MaxEdges; } int NumberOfVertices ( ) { return VerticesList.last +1; } int NumberOfEdges ( ) { return CurrentEdges; }

Page 14: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

NameType GetValue ( int i ) { return i >= 0 && i <= VerticesList.last ? VerticesList.data[i] : NULL; } DistType GetWeight ( int v1, int v2 ); int GetFirstNeighbor ( int v ); int GetNextNeighbor ( int v1, int v2 ); void InsertVertex ( NameType & vertex ); void InsertEdge ( int v1, int v2, DistType weight ); void RemoveVertex ( int v ); void RemoveEdge ( int v1, int v2 );}

Page 15: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接矩阵实现的部分图操作邻接矩阵实现的部分图操作template <class NameType, class DistType>Graph<NameType, DistType> :: Graph( int sz) {// 构造函数 for ( int i = 0; i < sz; i++ ) for ( int j = 0; j < sz; j++ ) Edge[i][j] = 0; CurrentEdges = 0;}

template <class NameType, class DistType> DistType Graph<NameType, DistType> ::GetWeight( int v1, int v2 ) {// 给出以顶点 v1 和 v2 为两端点的边上的权值

Page 16: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

if ( v1 != -1 && v2 != -1 ) return Edge[v1][v2]; else return 0;}

template <class NameType, class DistType>int Graph<NameType, DistType>::GetFirstNeighbor ( const int v ) {// 给出顶点位置为 v 的第一个邻接顶点的位置 if ( v != -1 ) { for ( int col = 0; col < VerticesList.last; col++ )

if ( Edge[row][col] > 0 && Edge[row][col] < max ) return col; }

Page 17: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

return -1;}

template <class NameType, class DistType> int Graph<NameType, DistType> ::GetNextNeighbor ( int v1, int v2 ) {// 给出顶点 v1 的某邻接顶点 v2 的下一个邻接顶点 int col; if ( v1 != -1 && v2 != -1 ) { for ( col = v2+1; col < VerticesList.last; col++ ) if ( Edge[v1][col] > 0 && Edge[v1][col] < max ) return col; } return -1;}

Page 18: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接表 邻接表 (Adjacency List)(Adjacency List) 无向图的邻接表无向图的邻接表

把同一个顶点发出的边链接在同一个边链表把同一个顶点发出的边链接在同一个边链表中,链表的每一个结点代表一条边,叫做边结中,链表的每一个结点代表一条边,叫做边结点,结点中保存有与该边相关联的另一顶点的点,结点中保存有与该边相关联的另一顶点的顶点下标 顶点下标 destdest 和指向同一链表中下一个边结和指向同一链表中下一个边结点的指针 点的指针 linklink 。。

Page 19: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

有向图的邻接表和逆邻接表有向图的邻接表和逆邻接表

在有向图的邻接表中,第 在有向图的邻接表中,第 ii 个边链表链接的个边链表链接的边都是边都是顶点 顶点 ii 发出的边发出的边。也叫做。也叫做出边表出边表。。 在有向图的逆邻接表中,第 在有向图的逆邻接表中,第 ii 个边链表链接个边链表链接的边都是的边都是进入顶点进入顶点 ii 的边的边。也叫做。也叫做入边表入边表。。

Page 20: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

带权图的边结点中保存该边上的权值 带权图的边结点中保存该边上的权值 costcost 。。 顶点 顶点 ii 的边链表的表头指针的边链表的表头指针 adjadj 在顶点表的在顶点表的下标为 下标为 i i 的顶点记录中,该记录还保存了该的顶点记录中,该记录还保存了该顶点的其它信息。顶点的其它信息。 在邻接表的边链表中,在邻接表的边链表中,各个边结点的链入顺序各个边结点的链入顺序任意,视边结点输入次序而定。任意,视边结点输入次序而定。 设图中有 设图中有 n n 个顶点,个顶点, e e 条边,则条边,则用邻接表表用邻接表表示无向图时示无向图时,需要 ,需要 n n 个顶点结点,个顶点结点, 22e e 个边个边结点;用结点;用邻接表表示有向图时邻接表表示有向图时,若不考虑逆邻,若不考虑逆邻接表,只需 接表,只需 n n 个顶点结点,个顶点结点, e e 个边结点。个边结点。

Page 21: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接表表示的图的类定义邻接表表示的图的类定义const int DefaultSize = 10;template <class DistType> class Graph;template <class DistType> struct Edge {friend class Graph<NameType, DistType>; int dest; DistType cost; Edge<DistType> *link; Edge ( ) { } Edge ( int D, DistType C ) : dest (D), cost (C), link (NULL) { } int operator != ( Edge<DistType>& E ) const { return dest != E.dest; } }

Page 22: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

template <class NameType, class DistType>struct Vertex {friend class Graph<NameType, DistType>; NemeType data; Edge<DistType> *adj;}

template <class NameType, class DistType> class Graph {private: Vertex<NameType, DistType> *NodeTable; int NumVertices; int MaxVertices;

Page 23: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

int NumEdges; int GetVertexPos ( NameType & vertex );public: Graph ( int sz ); ~Graph ( ); int GraphEmpty ( ) const { return NumVertices == 0; } int GraphFull ( ) const { return NumVertices == MaxVertices; } NameType GetValue ( int i ) { return i >= 0 && i < NumVertices ?

NodeTable[i].data : NULL; } int NumberOfVertices ( ) { return NumVertices; }

Page 24: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

int NumberOfEdges ( ) { return NumEdges; } void InsertVertex ( NameType & vertex ); void RemoveVertex ( int v ); void InsertEdge ( int v1, int v2, DistType weight ); void RemoveEdge ( int v1, int v2 ); DistType GetWeight ( int v1, int v2 ); int GetFirstNeighbor ( int v ); int GetNextNeighbor ( int v1, int v2 );}

Page 25: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

网络 网络 (( 带权图带权图 ) ) 的邻接表的邻接表

邻接表的构造函数和析构函数邻接表的构造函数和析构函数template <class NameType, class DistType>Graph<NameType, DistType> ::Graph ( int sz = DefaultSize ) : NumVertices (0), MaxVertices (sz), NumEdges (0){

Page 26: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

int n, e, k, j; NameType name, tail, head; DistType weight; NodeTable = // 创建顶点表 new Vertex<Nametype>[MaxVertices]; cin >> n; // 输入顶点个数 for ( int i = 0; i < n; i++) // 输入各顶点信息

{ cin >> name; InsertVertex ( name ); } cin >> e; // 输入边条数 for ( i = 0; i < e; i++) { // 逐条边输入 cin >> tail >> head >> weight; k = GetVertexPos ( tail ); j = GetVertexPos ( head ); InsertEdge ( k, j, weight ); }

Page 27: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

}

template <class NameType, class DistType>Graph<NameType, DistType> :: ~Graph ( ) { for ( int i = 0; i < NumVertices; i++ ) { Edge<DistType> *p = NodeTable[i].adj; while ( p != NULL ) { // 逐条边释放 NodeTable[i].adj = p→link; delete p; p = NodeTable[i].adj; } } delete [ ] NodeTable; // 释放顶点表}

Page 28: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接表部分成员函数的实现邻接表部分成员函数的实现template <class NameType, class DistType>int Graph<NameType, DistType> ::GetVertexPos ( const NameType & vertex ) {// 根据顶点名根据顶点名 vertexvertex 查找该顶点在邻接表中的位置查找该顶点在邻接表中的位置 for ( int i =0; i < NumVertices; i++ ) if ( NodeTable[i].data == vertex ) return i; return -1;}

template <Class NameType, class DistType> int Graph<NameType, DistType> ::GetFirstNeighbor ( int v ) {

Page 29: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

// 查找顶点 查找顶点 v v 第一个邻接顶点在邻接表中的位置第一个邻接顶点在邻接表中的位置 if ( v != -1 ) { // 若顶点存在 Edge<DistType> *p = NodeTable[v].adj; if ( p != NULL ) return p→dest; } return -1; // 若顶点不存在}

template <Class NameType, class DistTypeType> int Graph<NameType, DistType> :: GetNextNeighbor ( int v1, int v2 ) {// 查找顶点 查找顶点 vv11 在邻接顶点 在邻接顶点 vv2 2 后下一个邻接顶点后下一个邻接顶点 if ( v1 != -1 ) { Edge<DistType> *p = NodeTable[v1].adj;

Page 30: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

while ( p != NULL ) { if ( p→dest == v2 && p→link != NULL )

return p→link→dest; // 返回下一个邻接顶点在邻接表中的位置

else p = p→link; } } return -1; // 没有查到下一个邻接顶点返回 -1}

Page 31: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

template <Class NameType, class DistType> DistType Graph<NameType, DistType> ::GetWeight ( int v1, int v2) {// 取两端点为 取两端点为 vv1 1 和 和 vv2 2 的边上的权值的边上的权值 if ( v1 != -1 && v2 != -1 ) { Edge<DistType> *p = NodeTable[v1].adj; while ( p != NULL ) {

if ( p→dest == v2 ) return p→cost; else p = p→link; } } return 0;}

Page 32: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接多重表 邻接多重表 (Adjacency Multilist)(Adjacency Multilist) 在邻接多重表中,每一条边只有一个边结点。为在邻接多重表中,每一条边只有一个边结点。为有关边的处理提供了方便。有关边的处理提供了方便。 无向图的情形无向图的情形

边结点的结构边结点的结构 mark vertex1 vertex2 path1 path2

其中,其中, mark mark 是记录是否处理过的标记;是记录是否处理过的标记; vertvertexex11 和和 vertexvertex22 是依附于该边的两顶点位置。是依附于该边的两顶点位置。pathpath11 域是链接指针,指向下一条依附于域是链接指针,指向下一条依附于顶点顶点vertexvertex11 的边;的边; pathpath22 也是链接指针,指向下也是链接指针,指向下一条依附于一条依附于顶点顶点 vertexvertex22 的边。需要时还可设的边。需要时还可设置一个存放与该边相关的权值的域 置一个存放与该边相关的权值的域 costcost 。。

Page 33: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

顶点结点的结构顶点结点的结构 存储顶点信息的结点表存储顶点信息的结点表以顺序表方式组织以顺序表方式组织,每,每一个顶点结点有两个数据成员:其中,一个顶点结点有两个数据成员:其中, data data 存存放与该顶点相关的信息,放与该顶点相关的信息, Firstout Firstout 是指示第一条是指示第一条依附于该顶点的边的指针。依附于该顶点的边的指针。 在邻接多重表中在邻接多重表中 , , 所有依附于同一个顶点的所有依附于同一个顶点的边都链接在同一个单链表中。边都链接在同一个单链表中。 从从顶点 顶点 ii 出发出发 , , 可以循链找到所有依附于该可以循链找到所有依附于该顶点的边,也可以找到它的所有邻接顶点。顶点的边,也可以找到它的所有邻接顶点。 邻接多重表的结构邻接多重表的结构

data Firstout

Page 34: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

有向图的情形有向图的情形 在用邻接表表示有向图时,有时需要同在用邻接表表示有向图时,有时需要同时使用邻接表和逆邻接表。用有向图的邻接多时使用邻接表和逆邻接表。用有向图的邻接多重表重表 (( 十字链表十字链表 )) 可把这两个表结合起来表示。可把这两个表结合起来表示。

边结点的结构边结点的结构 mark vertex1 vertex2 path1 path2

Page 35: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

其中,其中, markmark 是处理标记;是处理标记; vertexvertex11 和和 vertexvertex22指明该有向边始顶点和终顶点的位置。指明该有向边始顶点和终顶点的位置。 pathpath11是指向始顶点与该边相同的下一条边的指针;是指向始顶点与该边相同的下一条边的指针; ppathath22 是指向终顶点与该边相同的下一条边的指是指向终顶点与该边相同的下一条边的指针。需要时还可有权值域针。需要时还可有权值域 costcost 。。

顶点结点的结构顶点结点的结构 每个顶点有一个结点,它相当于出边表和入每个顶点有一个结点,它相当于出边表和入边表的表头结点:其中,数据成员边表的表头结点:其中,数据成员 datadata 存放与存放与该顶点相关的信息,指针该顶点相关的信息,指针 FirstinFirstin 指示以该顶点指示以该顶点为始顶点的出边表的第一条边,为始顶点的出边表的第一条边, FirstoutFirstout 指示指示以该顶点为终顶点的入边表的第一条边。以该顶点为终顶点的入边表的第一条边。

data Firstin Firstout

Page 36: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

邻接多重表的结构邻接多重表的结构

Page 37: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的遍历与连通性图的遍历与连通性 从已给的连通图中某一顶点出发,沿着一些边访从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历 就叫做图的遍历 ( Graph Traversal )( Graph Traversal ) 。。 图中可能存在回路,且图的任一顶点都可能与其图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。某些边又回到了曾经访问过的顶点。 为了避免重复访问,可设置一个标志顶点是否被为了避免重复访问,可设置一个标志顶点是否被访问过的辅助数组 访问过的辅助数组 visitedvisited [ ] [ ] ,它的初始状态为 ,它的初始状态为

00 ,在图的遍历过程中,一旦某一个顶点 ,在图的遍历过程中,一旦某一个顶点 ii 被访被访问,就立即让 问,就立即让 visitedvisited [ [ii]] 为 为 11 ,防止它被多次,防止它被多次访问。访问。

Page 38: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

深度优先搜索深度优先搜索 DFS DFS ( Depth First Search )( Depth First Search ) 深度优先搜索的示例深度优先搜索的示例

Page 39: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

DFS DFS 在访问图中某一起始顶点 在访问图中某一起始顶点 v v 后,由 后,由 v v 出发,访问它的任一邻接顶点 出发,访问它的任一邻接顶点 ww11 ;再从 ;再从 ww1 1 出发,访问与 出发,访问与 ww11 邻 接但还没有访问过的顶点 邻 接但还没有访问过的顶点 ww22 ;然后再从 ;然后再从 ww2 2 出发,进行类似的访问,… 出发,进行类似的访问,… 如此进行下去,直至到达所有的邻接顶点都被如此进行下去,直至到达所有的邻接顶点都被访问过的顶点 访问过的顶点 u u 为止。接着,退回一步,退为止。接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。如果有,则访问此顶点,有被访问的邻接顶点。如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;之后再从此顶点出发,进行与前述类似的访问;如果没有,就再退回一步进行搜索。重复上述如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。过程,直到连通图中所有顶点都被访问过为止。

Page 40: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

图的深度优先搜索算法图的深度优先搜索算法template<class NameType, class DistType>void Graph <NameType, DistType> :: DFS ( ) { int * visited = new int [NumVertices]; for ( int i = 0; i < NumVertices; i++ ) visited [i] = 0; // 访问标记数组 visited 初始化 DFS (0, visited ); delete [ ] visited; // 释放 visited }

template<class NameType, class DistType>void Graph<NameType, DistType> ::DFS ( const int v, int visited [ ] ) {

Page 41: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

cout << GetValue (v) << ‘ ’; // 访问顶点 v visited[v] = 1; // 顶点 v 作访问标记 int w = GetFirstNeighbor (v); // 取 v 的第一个邻接顶点 w while ( w != -1 ) { // 若邻接顶点 w 存在 if ( !visited[w] ) DFS ( w, visited ); // 若顶点 w 未访问过 , 递归访问顶点 w w = GetNextNeighbor ( v, w ); // 取顶点 v 的排在 w 后面的下一个邻接顶点 }}

Page 42: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

算法分析算法分析 图中有 图中有 n n 个顶点,个顶点, e e 条边。条边。 如果用邻接表表示图,沿如果用邻接表表示图,沿 Firstout Firstout link link 链链可以找到某个顶点 可以找到某个顶点 v v 的所有邻接顶点 的所有邻接顶点 ww 。。由于总共有 由于总共有 22e e 个边结点,所以扫描边的时个边结点,所以扫描边的时间为间为 O(O(ee)) 。而且对所有顶点递归访问。而且对所有顶点递归访问 11 次,次,所以遍历图的时间复杂性为所以遍历图的时间复杂性为 O(O(nn++ee)) 。。 如果用邻接矩阵表示图,则查找每一个顶点的如果用邻接矩阵表示图,则查找每一个顶点的所有的边,所需时间为所有的边,所需时间为 O(O(nn)) ,则遍历图中所,则遍历图中所有的顶点所需的时间为有的顶点所需的时间为 O(O(nn22)) 。。

Page 43: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

广度优先搜索广度优先搜索 BFS BFS ( Breadth First Search( Breadth First Search )) 广度优先搜索的示例广度优先搜索的示例

广度优先搜索过程广度优先搜索过程 广度优先生成树 广度优先生成树

Page 44: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

使用广度优先搜索在访问了起始顶点 使用广度优先搜索在访问了起始顶点 v v 之后,之后,由 由 v v 出发,依次访问 出发,依次访问 v v 的各个未曾被访问的各个未曾被访问过的邻接顶点 过的邻接顶点 ww11, , ww22, …, , …, wwtt ,然后再顺序访,然后再顺序访问 问 ww11, , ww22, …, , …, wwt t 的所有还未被访问过的邻接的所有还未被访问过的邻接顶点。再从这些访问过的顶点出发,再访问它顶点。再从这些访问过的顶点出发,再访问它们的所有还未被访问过的邻接顶点,… 如此们的所有还未被访问过的邻接顶点,… 如此做下去,直到图中所有顶点都被访问到为止。做下去,直到图中所有顶点都被访问到为止。

广度优先搜索是一种分层的搜索过程,每向前广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况。因此,广度优先搜索不那样有往回退的情况。因此,广度优先搜索不是一个递归的过程,其算法也不是递归的。是一个递归的过程,其算法也不是递归的。

Page 45: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

为了实现逐层访问,算法中使用了一个队列,为了实现逐层访问,算法中使用了一个队列,以记忆正在访问的这一层和上一层的顶点,以以记忆正在访问的这一层和上一层的顶点,以便于向下一层访问。便于向下一层访问。 为避免重复访问,需要一个辅助数组 为避免重复访问,需要一个辅助数组 visitedvisited

[ ][ ] ,给被访问过的顶点加标记。,给被访问过的顶点加标记。 图的广度优先搜索算法图的广度优先搜索算法template<class NameType, class DistType>void Graph <NameType, DistType> ::BFS ( int v ) { int * visited = new int[NumVertices]; for ( int i = 0; i < NumVertices; i++ ) visited[i] = 0; //visited 初始化

Page 46: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

cout << GetValue (v) << ' '; visited[v] = 1; Queue<int> q; q.EnQueue (v); // 访问 v, 进队列 while ( !q.IsEmpty ( ) ) { //队空搜索结束 v = q.DeQueue ( ); // 不空 , 出队列 int w = GetFirstNeighbor (v); // 取顶点 v 的第一个邻接顶点 w while ( w != -1 ) { // 若邻接顶点 w 存在

if ( !visited[w] ) { // 若该邻接顶点未访问过 cout << GetValue (w) << ‘ ’; // 访问 visited[w] = 1; q.EnQueue (w); // 进队

} w = GetNextNeighbor (v, w);

// 取顶点 v 的排在 w 后面的下一邻接顶点

Page 47: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

} // 重复检测 v 的所有邻接顶点 } //外层循环,判队列空否 delete [ ] visited; }

算法分析算法分析 如果使用邻接表表示图,则循环的总时间代价如果使用邻接表表示图,则循环的总时间代价为 为 dd00 + d + d11 + … + d + … + dnn-1-1 = O( = O(ee)) ,其中的 ,其中的 ddi i 是顶是顶点 点 i i 的度。的度。 如果使用邻接矩阵,则对于每一个被访问过的如果使用邻接矩阵,则对于每一个被访问过的顶点,循环要检测矩阵中的 顶点,循环要检测矩阵中的 n n 个元素,总的时个元素,总的时间代价为间代价为 O(O(nn22)) 。。

Page 48: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

连通分量 连通分量 (Connected component)(Connected component) 当无向图为非连通图时,从图中某一顶点出发,当无向图为非连通图时,从图中某一顶点出发,利用深度优先搜索算法或广度优先搜索算法不利用深度优先搜索算法或广度优先搜索算法不可能遍历到图中的所有顶点,只能访问到该顶可能遍历到图中的所有顶点,只能访问到该顶点所在的最大连通子图点所在的最大连通子图 (( 连通分量连通分量 )) 的所有顶点。的所有顶点。 若从无向图的每一个连通分量中的一个顶点出若从无向图的每一个连通分量中的一个顶点出发进行遍历,可求得无向图的所有连通分量。发进行遍历,可求得无向图的所有连通分量。 在算法中,需要对图的每一个顶点进行检测:在算法中,需要对图的每一个顶点进行检测:若已被访问过,则该顶点一定是落在图中已求若已被访问过,则该顶点一定是落在图中已求得的连通分量上;若还未被访问,则从该顶点得的连通分量上;若还未被访问,则从该顶点出发遍历图,可求得图的另一个连通分量。出发遍历图,可求得图的另一个连通分量。

Page 49: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

对于非连通的无向图,所有连通分量的生成树对于非连通的无向图,所有连通分量的生成树组成了非连通图的生成森林。组成了非连通图的生成森林。

Page 50: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

确定连通分量的算法确定连通分量的算法 template<class NameType, class DistType>void Graph<NameType, DistType> ::Components ( ) { int *visited = new int[NumVertices]; for ( int i = 0; i < NumVertices; i++ ) visited[i] = 0; //visited 初始化 for ( i = 0; i < NumVertices; i++ ) if ( !visited[i] ) { //检测所有顶点是否访问过

DFS ( i, visited ); // 从未访问的顶点出发访问 OutputNewComponent ( ); // 输出一个连通分量

}

Page 51: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

delete [ ] visited; // 释放 visited}

重连通分量 重连通分量 (Biconnected Componen(Biconnected Component)t) 在无向连通图在无向连通图 GG 中,当且仅当删去中,当且仅当删去 GG 中的中的顶点 顶点 vv 及及所有依附于所有依附于 vv 的所有边的所有边后,可将图分割成后,可将图分割成 两个或两个以上的连通分量,则称顶点两个或两个以上的连通分量,则称顶点 vv为为关关 节点节点。。 没有没有关节点关节点的连通图叫做重连通图。的连通图叫做重连通图。 在重连通图上在重连通图上 , , 任何一对顶点之间至少存在任何一对顶点之间至少存在有有 两条路径两条路径 , , 在删去某个顶点及与该顶点相在删去某个顶点及与该顶点相关联关联 的边时的边时 , , 也不破坏图的连通性。也不破坏图的连通性。

Page 52: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

一个连通图一个连通图 GG 如果不是重连通图,那么它可以包括几个如果不是重连通图,那么它可以包括几个重连通分量。重连通分量。 在一个无向连通图在一个无向连通图 GG 中中 , , 重连通分量可以利用深度优先重连通分量可以利用深度优先生成树找到。生成树找到。 dfn dfn 顶点的深度优先数,标明进行深度优先搜索时各顶顶点的深度优先数,标明进行深度优先搜索时各顶点访问的次序。点访问的次序。 如果在深度优先生成树中,如果在深度优先生成树中,顶点 顶点 u u 是是顶点 顶点 v v 的祖先的祖先 , , 则有则有 dfndfn[[uu] < ] < dfndfn[[vv]] 。。 深度优先生成树的根是深度优先生成树的根是关节点关节点的充要条件是的充要条件是它至少有两它至少有两个子女个子女。。 其它顶点 其它顶点 u u 是是关节点关节点的充要条件是的充要条件是它至少有一个子女 它至少有一个子女

ww, , 从 从 w w 出发出发 , , 不能通过 不能通过 ww 、、 w w 的子孙及一条回边所的子孙及一条回边所组成的路径到达 组成的路径到达 u u 的祖先的祖先。。

Page 53: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结
Page 54: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在图在图 GG 的每一个顶点上定义一个 的每一个顶点上定义一个 low low 值,值, lowlow[[uu] ] 是从 是从 u u 或 或 u u 的子孙的子孙出发通过出发通过回边回边可以到可以到达的达的最低深度优先数最低深度优先数。。

low[u] = min { dfn[u], min{ low[w] | w 是 是 u u 的一个子女的一个子女 }, min{ dfn[x] | (u, x) 是一条回边是一条回边 } } u u 是关节点的充要条件是:是关节点的充要条件是:

u u 是具有两个以上子女的生成树的根是具有两个以上子女的生成树的根 uu 不是根,但它有一个子女 不是根,但它有一个子女 ww ,使得,使得 lowlow[[ww] ] dfndfn[[uu]]

这时 这时 w w 及其子孙不存在指向顶点及其子孙不存在指向顶点 uu 的祖先的回的祖先的回边。边。

Page 55: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

计算计算 dfndfn 与与 lowlow 的算法 的算法 (1)(1)template<class NameType, class DistType>void Graph<NameType, DistType> ::DfnLow ( const int x ) {//公有函数:从顶点 x开始深度优先搜索 int num = 1; // num 是访问计数器 static int * dfn = new int[NumVertices]; static int * low = new int[NumVertices]; //dfn 是深度优先数 , low 是最小祖先访问顺序号 for ( int i = 0; i < NumVertices; i++ ) { dfn[i] = low[i] = 0; } // 给予访问计数器 num 及 dfn[u], low[u]初值 DfnLow ( x, -1 ); // 从根 x开始

Page 56: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

delete [ ] dfn; delete [ ] low;}

计算计算 dfndfn 与与 lowlow 的算法 的算法 (2)(2)

template<class NameType, class DistType>void Graph<NameType, DistType> ::DfnLow ( int u, int v ) {//私有函数:从顶点 u 开始深度优先搜索计算 dfn// 和 low 。在产生的生成树中 v 是 u 的双亲。 dfn[u] = low[u] = num++; int w = GetFirstNeighbor (u); while ( w != -1 ) { // 对 u 所有邻接顶点 w 循环

Page 57: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

if ( dfn[w] == 0 ) { //未访问过 , w 是 u 的孩子 DfnLow ( w, u ); // 从 w递归深度优先搜索 low[u] = min2 ( low[u], low[w] );

// 子女 w 的 low[w]先求出 , 再求 low[u]

} else if ( w != v ) //w 访问过且 w 不是 v, 是回边 low[u] = min2 ( low[u], dfn[w] ); // 根据回边另一顶点 w调整 low[u] w = GetNextNeighbor ( u, w ); // 找顶点 u 在 w 后面的下一个邻接顶点 }}

Page 58: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在算法在算法 DfnLowDfnLow 增加一些语句增加一些语句 , , 可把连通图的边可把连通图的边划分到各重连通分量中。首先划分到各重连通分量中。首先 , , 根据 根据 DfnLow DfnLow ((ww, , uu)) 的返回的返回 , , 计算计算 lowlow[[ww]] 。如果。如果 lowlow[[ww] ] dfndfn[[uu]] ,,则开始计算新的重连通分量。则开始计算新的重连通分量。 在算法中利用一个栈, 在遇到一条边时保在算法中利用一个栈, 在遇到一条边时保存它。在函数存它。在函数 BiconnectedBiconnected 中就能输出一个重连通中就能输出一个重连通分量的所有的边。分量的所有的边。 当 当 n n > 1> 1 时输出重连通分量时输出重连通分量(( 11 ))template<class NameType, class DistType>void Graph<NameType, DistType> ::Biconnected ( ) {

Page 59: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

//公有函数:从顶点 0开始深度优先搜索 int num = 1; // 访问计数器 num static int * dfn = new int[NumVertices]; static int * low = new int[NumVertices]; // dfn 是深度优先数 , low 是最小祖先号 for ( int i = 0; i < NumVertices; i++ ) { dfn[i] = low[i] = 0; } DfnLow ( 0, -1 ); // 从顶点 0 开始 delete [ ] dfn; delete [ ] low;}

当 当 nn > 1 > 1 时输出重连通分量(时输出重连通分量( 22))

Page 60: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

template<class NameType, class DistType> void Graph<NameType, DistType> ::Biconnected ( int u, int v ) {// 计算 dfn 与 low, 根据其重连通分量输出 Graph的// 边。在产生的生成树中 , v 是 u 的双亲结点 , S // 是一个初始为空的栈,应声明为图的数据成员。 int x, y, w; dfn[u] = low[u] = num++; w = GetFirstNeighbor (u); // 找顶点 u 的第一个邻接顶点 w while ( w != - 1 ) { if ( v != w && dfn[w] < dfn[u] ) S.Push ((u,w)); //w 不是 u 的双亲且 w先于 u 被访问 , (u,w)进栈

Page 61: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

if ( dfn[w] == 0 ) { //未访问过 , w 是 u 的孩子 Biconnected (w, u); // 从 w递归深度优先访问 low[u] = min2 ( low[u], low[w] );

// 根据先求出的 low[w], 调整 low[u] if ( low[w] >= dfn[u] ) { // 无回边 , 原来的重连通分量结束 cout << “新重连通分量 : ” << end1; do { (x, y) = S.Pop ( ); cout << x << "," << y << endl;

} while ( (x, y) 与 (u, w) 不是同一条边 );} // 输出该重连通分量的各边

} else if ( w != v ) // 有回边 , 计算

Page 62: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

low[u] = min2 ( low[u], dfn[w] ); // 根据回边另一顶点 w调整 low[u] w = GetNextNeighbor ( u, w ); // 找顶点 u 的邻接顶点 w 的下一个邻接顶点 }}

算法 算法 BiconnectedBiconnected 的时间代价是 的时间代价是 O(O(nn++ee)) 。。其中 其中 nn 是该连通图的顶点数,是该连通图的顶点数, e e 是该连通图的是该连通图的边数。边数。 此算法的前提条件是连通图中至少有两个顶此算法的前提条件是连通图中至少有两个顶点点 ,, 因为正好有一个顶点的图连一条边也没有。因为正好有一个顶点的图连一条边也没有。

Page 63: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

最小生成树 最小生成树 ( minimum cost spanning tree )( minimum cost spanning tree )

使用不同的遍历图的方法,可以得到不同的生使用不同的遍历图的方法,可以得到不同的生成树;从不同的顶点出发,也可能得到不同的成树;从不同的顶点出发,也可能得到不同的生成树。生成树。 按照生成树的定义,按照生成树的定义, n n 个顶点的连通网络的生个顶点的连通网络的生成树有 成树有 n n 个顶点、个顶点、 n-n-1 1 条边。条边。 构造最小生成树的准则构造最小生成树的准则

必须只使用该网络中的边来构造最小生成树;必须只使用该网络中的边来构造最小生成树; 必须使用且仅使用 必须使用且仅使用 nn--1 1 条边来联结网络中条边来联结网络中的 的 n n 个顶点;个顶点; 不能使用产生回路的边。不能使用产生回路的边。

Page 64: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

克鲁斯卡尔 克鲁斯卡尔 (Kruskal) (Kruskal) 算法算法 克鲁斯卡尔算法的基本思想:克鲁斯卡尔算法的基本思想: 设有一个有 设有一个有 n n 个顶点的连通网络 个顶点的连通网络 NN = { = {

VV, , EE } },, 最初先构造一个只有 最初先构造一个只有 n n 个顶点,没有边个顶点,没有边的非连通图 的非连通图 TT = { = { VV, , } }, , 图中每个顶点自成一图中每个顶点自成一个连通分量。当在 个连通分量。当在 E E 中选到一条具有最小权值中选到一条具有最小权值的边时的边时 ,, 若该边的两个顶点落在不同的连通分量若该边的两个顶点落在不同的连通分量上,则将此边加入到 上,则将此边加入到 T T 中;否则将此边舍去,中;否则将此边舍去,重新选择一条权值最小的边。如此重复下去,直重新选择一条权值最小的边。如此重复下去,直到所有顶点在同一个连通分量上为止。到所有顶点在同一个连通分量上为止。 算法的框架算法的框架 我们利用最小堆我们利用最小堆 (MinHeap)(MinHeap) 和并和并查集查集 (DisjointSets)(DisjointSets) 来实现克鲁斯卡尔算法。来实现克鲁斯卡尔算法。

Page 65: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

首先,利用最小堆来存放首先,利用最小堆来存放 EE 中的所有的边中的所有的边 , , 堆堆中每个结点的格式为中每个结点的格式为

在构造最小生成树的过程中,最小堆中存放剩在构造最小生成树的过程中,最小堆中存放剩余的边,并且利用并查集的运算检查依附于一余的边,并且利用并查集的运算检查依附于一条边的两个顶点 条边的两个顶点 tailtail 、、 head head 是否在同一个连是否在同一个连通分量 通分量 (( 即并查集的同一个子集合即并查集的同一个子集合 ) ) 上上 , , 是是则舍去这条边;否则将此边加入 则舍去这条边;否则将此边加入 TT ,同时将,同时将这两个顶点放在同一个连通分量上。随着各边这两个顶点放在同一个连通分量上。随着各边逐步加入到最小生成树的边集合中,各连通分逐步加入到最小生成树的边集合中,各连通分量也在逐步合并,直到形成一个连通分量为止。量也在逐步合并,直到形成一个连通分量为止。

tail head cost 边的两个顶点位置 边的权值

Page 66: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

应用克鲁斯卡尔算法构造最小生成树的过程应用克鲁斯卡尔算法构造最小生成树的过程

Page 67: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

最小生成树类定义最小生成树类定义const int MAXINT = 机器可表示的 , 问题中不可 能出现的大数class MinSpanTree;

class MSTEdgeNode { // 生成树边结点类定义friend class MinSpanTree;private: int tail, head; // 生成树各边的两顶点 float cost; // 生成树各边的代价};

Page 68: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

class MinSpanTree { // 生成树的类定义public: MinSpanTree ( int sz = NumVertices -1 ) :

MaxSize (sz), n (0) { edgevalue = new MSTEdgeNode[MaxSize]; } int Insert ( MSTEdgeNode & item ); //将 item加到最小生成树中protected: MSTEdgeNode *edgevalue; // 边值数组 int MaxSize, n; // 最大边数 , 当前边数};

Page 69: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

利用克鲁斯卡尔算法建立最小生成树利用克鲁斯卡尔算法建立最小生成树void Graph<string, float> ::Kruskal ( MinSpanTree& T ) { MSTEdgeNode e; // 边结点辅助单元 MinHeap<MstEdgeNode> H(CurrentEdges); int NumVertices = VerticesList.last; // 顶点个数 UFSets F (NumVertices); //并查集 F 并初始化 for ( int u = 0; u < NumVertices; u++ ) // 邻接矩阵 for ( int v = u +1; v < NumVertices; v++ )

if ( Edge[u][v] != MAXNUM ) { // 所有边 e.tail = u; e.head = v;

e.cost = Edge[u][v]; H.Insert (e); //插入堆 }

Page 70: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

int count = 1; // 最小生成树加入边数的计数 while ( count < NumVertices ) { H.RemoveMin ( e ); // 从堆中退出一条边 u = F.Find ( e.tail ); //检测两端点的根

v = F.Find ( e.head );if ( u != v ) { // 根不同不在同一连通分量 F.Union ( u, v ); // 合并

T.Insert ( e ); // 该边存入最小生成树 T count++; } }}

Page 71: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

出堆顺序出堆顺序 (0,5,10) 选中 (2,3,12) 选中 (1,6,14) 选中 (1,2,16) 选中 (3,6,18) 舍弃 (3,4,22) 选中 (4,6,24) 舍弃 (4,5,25) 选中

并查集 邻接矩阵表示

-2-2-2

-2

-1 -1 -1 -1 -1 -1 -1-1 -1 -1 -1 -10

2-1 -1 -10-2 2 0

000

0 1 2 3 4 5 6

-2 1-11-2 -1-4 21

-2 -5 1 2 11-71 1 2 1 1

F 0 1 2 3 4 5 6 0 28 10 028 0 16 14 1 16 0 12 2 12 0 22 18 3 22 0 25 24 410 25 0 5 14 18 24 0 6

Page 72: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在建立最小堆时需要检测邻接矩阵,计算的时在建立最小堆时需要检测邻接矩阵,计算的时间代价为 间代价为 O(O(nn22)) 。且做了 。且做了 ee 次堆插入操作次堆插入操作 , , 每次插入调用了一个堆调整算法每次插入调用了一个堆调整算法 FilterUpFilterUp( )( )算算法法 , , 因此堆插入需要的时间代价为 因此堆插入需要的时间代价为 O(O(eeloglog22ee)) 。。 在构造最小生成树时,最多调用了 在构造最小生成树时,最多调用了 e e 次出堆操次出堆操作作 RemoveMinRemoveMin(( )) ,, 22e e 次并查集的次并查集的 FindFind( )( ) 操作,操作,

nn-1-1 次次 UnionUnion( )( ) 操作,计算时间分别为 操作,计算时间分别为 O(O(eeloglog22

ee)) ,, O(O(eeloglog22nn)) 和和 O(O(nn)) 。。 总的计算时间为 总的计算时间为 O(O(eeloglog22ee + + eeloglog22nn + + n n22 + n + n ))

Page 73: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

普里姆普里姆 (Prim)(Prim) 算法算法 普里姆算法的基本思想:普里姆算法的基本思想: 从连通网络 从连通网络 NN = { = { VV, , EE } } 中的某一顶点 中的某一顶点

uu00 出发,选择与它关联的具有最小权值的边出发,选择与它关联的具有最小权值的边 ((uu00, , vv)) ,将其顶点加入到,将其顶点加入到生成树的顶点集合生成树的顶点集合 UU 中。中。 以后每一步从以后每一步从一个顶点在一个顶点在 UU 中中,而,而另一个另一个顶点不在顶点不在 UU 中中的各条边中选择的各条边中选择权值最小的边权值最小的边 ((uu, , vv)),, 把它的顶点加入到把它的顶点加入到集合集合 UU 中。如此继续下去,中。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集直到网络中的所有顶点都加入到生成树顶点集合合 UU 中为止。中为止。

采用邻接矩阵作为图的存储表示。采用邻接矩阵作为图的存储表示。

Page 74: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

用普里姆算法构造最小生成树的过程用普里姆算法构造最小生成树的过程

Page 75: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在构造过程中,还设置了两个辅助数组:在构造过程中,还设置了两个辅助数组: lowcostlowcost[ ][ ] 存放存放生成树顶点集合内顶点生成树顶点集合内顶点到到生生成树外各顶点成树外各顶点的各边上的当前最小权值;的各边上的当前最小权值; nearvexnearvex[ ] [ ] 记录记录生成树顶点集合外各顶点生成树顶点集合外各顶点距距离离集合内哪个顶点集合内哪个顶点最近最近 (( 即权值最小即权值最小 )) 。。

例子例子

Page 76: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

若选择从顶点若选择从顶点 00 出发,即出发,即 uu00 = 0 = 0 ,则两个辅助数组的初始状态为:,则两个辅助数组的初始状态为:

然后反复做以下工作:然后反复做以下工作: 在 在 lowcost lowcost [ ][ ] 中选择中选择 nearvexnearvex[[ii] ] --11 &&&& lowcostlowcost[[ii]] 最小最小的边的边 ii 用 用 vv 标记它。则选中的权值最小的边为 标记它。则选中的权值最小的边为 ((nearvexnearvex[[vv], ], vv)) ,,相应的权值为 相应的权值为 lowcostlowcost[[vv]] 。。 将 将 nearvexnearvex[[vv] ] 改为 改为 --11, , 表示它已加入生成树顶点集合。将边 表示它已加入生成树顶点集合。将边

( ( nearvexnearvex[[vv], ], vv, , lowcostlowcost[[vv] ) ] ) 加入生成树的边集合。加入生成树的边集合。

Page 77: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

取 取 lowcostlowcost[[ii] = min{ ] = min{ lowcostlowcost[[ii], ], EdgeEdge[[vv][][ii]] }} ,,即用即用生成树顶点集合外各顶点生成树顶点集合外各顶点 ii 到到刚加入该集刚加入该集合的新顶点 合的新顶点 vv 的距离 的距离 EdgeEdge[[vv][][ii]] 与原来它们到与原来它们到生成树顶点集合中顶点的最短距离生成树顶点集合中顶点的最短距离 lowcostlowcost[[ii]] 做做比较比较 , , 取距离近的作为这些集合外顶点到生成取距离近的作为这些集合外顶点到生成树顶点集合内顶点的最短距离。树顶点集合内顶点的最短距离。 如果如果生成树顶点集合外顶点 生成树顶点集合外顶点 i i 到到刚加入该集刚加入该集合的新顶点 合的新顶点 v v 的距离比原来它到生成树顶点集的距离比原来它到生成树顶点集合中顶点的最短距离还要近,则修改合中顶点的最短距离还要近,则修改 nearvexnearvex[[ii]] ::

nearvexnearvex[[ii] = ] = vv 。表示生成树外顶点。表示生成树外顶点 ii 到生成树内到生成树内顶点顶点 vv 当前距离最近。当前距离最近。

Page 78: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

继续重复得:

顶点 5加入生成树顶点集合:v = 5

v = 4

Page 79: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

最后生成树中边集合里存入得各条边为最后生成树中边集合里存入得各条边为::(0, 5, 10), (5, 4, 25), (4, 3, 22),(3, 2, 12), (2, 1, 16), (1, 6, 14)

利用普里姆算法建立最小生成树利用普里姆算法建立最小生成树void Graph<string, float> ::Prim ( MinSpanTree &T ) { int NumVertices = VerticesList.last; // 图顶点数 float * lowcost = new float[NumVertices]; int * nearvex = new int[NumVertices]; for ( int i = 1; i < NumVertices; i++ ) { lowcost[i] = Edge[0][i]; // 顶点 0 到各边的代价 nearvex[i] = 0; // 及最短带权路径 }

Page 80: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

nearvex[0] = -1; // 顶点 0加到生成树顶点集合 MSTEdgeNode e; // 最小生成树结点辅助单元 for ( i = 1; i < NumVertices; i++ ) { // 循环 n-1 次 , 加入 n-1 条边 float min = MAXNUM; int v = 0; for ( int j = 0; j < NumVertices; j++ ) if ( nearvex[j] != -1 && lowcost[j] < min ) { v = j; min = lowcost[j]; } //求生成树外顶点到生成树内顶点具有最小 // 权值的边 , v 是当前具最小权值的边的位置 if ( v ) { //v==0 表示再也找不到要求顶点了 e.tail = nearvex[v]; e.head = v;

Page 81: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

e.cost = lowcost[v]; T.Insert (e); //选出的边加入生成树 nearvex[v] = -1; // 作该边已加入生成树标记 for ( j = 1; j < NumVertices; j++ ) if ( nearvex[j] != -1 && // j 不在生成树中

Edge[v][j] < lowcost[j] ) { // 需要修改 lowcost[j] = Edge[v][j]; nearvex[j] = v; } } }}

分析以上算法,设连通网络有 分析以上算法,设连通网络有 n n 个顶点个顶点 ,则该算法的时间复杂,则该算法的时间复杂度为 度为 O(O(nn22), ), 它适用于边稠密的网它适用于边稠密的网络。络。

Page 82: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

最短路径 最短路径 (Shortest Path)(Shortest Path) 最短路径问题:最短路径问题:如果从图中某一顶点如果从图中某一顶点 (( 称为源称为源点点 )) 到达另一顶点到达另一顶点 (( 称为终点称为终点 )) 的路径可能不止的路径可能不止一条,如何找到一条路径使得沿此路径上各边上一条,如何找到一条路径使得沿此路径上各边上的权值总和达到最小。的权值总和达到最小。 问题解法问题解法

边上权值非负情形的单源最短路径问题边上权值非负情形的单源最短路径问题 — — DijkstraDijkstra 算法算法 边上权值为任意值的单源最短路径问题边上权值为任意值的单源最短路径问题 — — BellmanBellman 和和 FordFord 算法算法 所有顶点之间的最短路径所有顶点之间的最短路径 — — FloydFloyd 算法算法

Page 83: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

边上权值非负情形的单源最短路径问题边上权值非负情形的单源最短路径问题 问题的提法: 问题的提法: 给定一个带权有向图给定一个带权有向图 DD 与源点与源点 vv ,,求从求从 vv 到到 DD 中其它顶点的最短路径。中其它顶点的最短路径。限定各边上限定各边上的权值大于或等于的权值大于或等于 00 。。

为求得这些最短路径,为求得这些最短路径, DijkstraDijkstra 提出按路径长度提出按路径长度的递增次序,逐步产生最短路径的算法。首先求的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条最短路径,再参照它求出长度出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从顶点次短的一条最短路径,依次类推,直到从顶点 vv到其它各顶点的最短路径全部求出为止。到其它各顶点的最短路径全部求出为止。

举例说明举例说明

Page 84: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

DijkstraDijkstra 逐步求解的过程逐步求解的过程源点 终点 最 短 路 径 路径长度 v0 v1 (v0, v1) 10

v2 — (v0, v1, v2) (v0, v3, v2) — 60 50 v3 (v0, v3) 30 v4 (v0, v4) (v0, v3, v4) (v0, v3, v2, v4) 100 0 60

Page 85: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

引入一个辅助数组引入一个辅助数组 distdist 。它的每一个分量。它的每一个分量 distdist[[ii]] 表表示当前找到的从示当前找到的从源点源点 vv00 到到终点终点 vvii 的最短路径的长度。的最短路径的长度。初始状态:初始状态: 若从源点若从源点 vv00 到顶点到顶点 vvii 有边,则有边,则 distdist[[ii]] 为该边上的为该边上的权值;权值;

若从源点若从源点 vv00 到顶点到顶点 vvii 没有边,则没有边,则 distdist[[ii]] 为为 ++ 。。 一般情况下,假设 一般情况下,假设 S S 是已求得的最短路径的终点是已求得的最短路径的终点的集合,则可证明:的集合,则可证明:下一条最短路径必然是从下一条最短路径必然是从 vv0 0 出出发,中间只经过发,中间只经过 SS 中的顶点便可到达的那些顶点中的顶点便可到达的那些顶点 vvxx

((vvxx V-SV-S ) ) 的路径中的一条。的路径中的一条。 每次求得一条最短路径之后,其终点每次求得一条最短路径之后,其终点 vvkk 加入集合加入集合 SS ,,然后然后对所有的对所有的 vvii V-SV-S ,修改其,修改其 distdist[[ii]] 值。值。

Page 86: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

DijkstraDijkstra 算法可描述如下:算法可描述如下: 初始化: 初始化: SS ← { ← { vv0 0 }; }; distdist[[jj] ← ] ← EdgeEdge[0][[0][jj], ], jj = 1, 2, …, = 1, 2, …, nn-1;-1; // // nn 为图中顶点个数为图中顶点个数 求出最短路径的长度:求出最短路径的长度: distdist[[kk] ← min{ ] ← min{ distdist[[ii] }, ] }, ii V- S V- S ; ; SS ← ← SS UU { { kk };}; 修改: 修改: distdist[[ii] ← min{ ] ← min{ distdist[[ii], ], distdist[[kk] +] + Edge Edge[[kk][][ii] },] }, 对于每一个 对于每一个 ii V- S V- S ; ; 判断: 若判断: 若 S = VS = V, , 则算法结束,否则转则算法结束,否则转。。

Page 87: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

用于计算最短路径的图邻接矩阵类的定义用于计算最短路径的图邻接矩阵类的定义const int NumVertices = 6; // 图中最大顶点个数class Graph { // 图的类定义 private: float Edge[NumVertices][NumVertices]; float dist[NumVertices]; // 最短路径长度数组 int path[NumVertices]; // 最短路径数组 int S[NumVertices]; // 最短路径顶点集public: void ShortestPath ( int, int ); int choose ( int );};

Page 88: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

计算从单个顶点到其它各个顶点的最短路径计算从单个顶点到其它各个顶点的最短路径void Graph :: ShortestPath ( int n, int v ){//Graph 是一个具有 n 个顶点的带权有向图 , 各边// 上的权值由 Edge[i][j] 给出。本算法建立起一个// 数组 : dist[j], 0 j < n, 是当前求到的从顶点 v //// 到顶点 j 的最短路径长度 , 同时用数组 path[j], // 0 j < n, 存放求到的最短路径。 for ( int i = 0; i < n; i++) { dist[i] = Edge[v][i]; //dist 数组初始化 S[i] = 0; if ( i != v && dist[i] < MAXNUM) path[i] = v; else path[i] = -1; //path 数组初始化 }

Page 89: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

S[v] = 1; dist[v] = 0; // 顶点 v加入顶点集合 //选择当前不在集合 S 中具有最短路径的顶点 u for ( i = 0; i < n-1; i++ ) { float min = MAXNUM; int u = v; for ( int j = 0; j < n; j++ )

if ( !S[j] && dist[j] < min ) { u = j; min = dist[j]; } S[u] = 1; //将顶点 u加入集合 S for ( int w = 0; w < n; w++ ) //修改

if ( !S[w] && Edge[u][w] < MAXNUM && dist[u] + Edge[u][w] < dist[w] ) {

dist[w] = dist[u] + Edge[u][w]; path[w] = u; }

Page 90: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

}}

算法的时间复杂度算法的时间复杂度:: O(O(nn22))

Page 91: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

DijkstraDijkstra 算法中各辅助数组的变化算法中各辅助数组的变化

如何从表中读取源点 0 到终点 v 的最短路径?举顶点 4为例 path[4] = 2 →path[2] = 3 → path[3] = 0 ,反过来排列,得到路径 0, 3, 2, 4 ,这就是源点 0 到终点 4 的最短路径。

选取 顶点 1 顶点 2 顶点 3 顶点 4

终点 S[1] d[1] p[1] S[2] d[2] p[2] S[3] d[3] p[3] S[4] d[4] p[4]

0 0 10 0 0 0 0 30 0 0 100 0

1 1 10 0 0 60 1 0 30 0 0 100 0

3 1 10 0 0 50 3 1 30 0 0 90 3

2 1 10 0 1 50 3 1 30 0 0 60 2

4 1 10 0 1 50 3 1 30 0 1 60 2

0

41

2 3

10

50 10

20

60

30

100

Page 92: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

边上权值为任意值的单源最短路径问题边上权值为任意值的单源最短路径问题 带权有向图带权有向图 DD 的某几条边或所有边的长度可能为的某几条边或所有边的长度可能为负值。利用负值。利用 DijkstraDijkstra算法,不一定能得到正确的算法,不一定能得到正确的结果。结果。

源点源点 00 到终点到终点 22 的最短路径应是的最短路径应是 0, 1, 20, 1, 2 ,其长度,其长度为为 22 ,它小于算法中计算出来的,它小于算法中计算出来的 distdist[2][2] 的值。的值。

若设源点若设源点 vv = 0 = 0 ,使用,使用DijkstraDijkstra 算法所得结果算法所得结果

选取 顶 点 0 顶 点 1 顶 点 2 顶点 S[0] d[0] p[0] S[1] d[1] p[1] S[2] d[2] p[2] 0 1 0 -1 0 7 0 0 5 0 2 1 0 -1 0 7 0 1 5 0 1 1 0 -1 1 7 0 1 5 0

0 1 1

5

7 -5

Page 93: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

BellmanBellman 和和 FordFord 提出了从源点逐次绕过其它提出了从源点逐次绕过其它顶点,以缩短到达终点的最短路径长度的方法。顶点,以缩短到达终点的最短路径长度的方法。该方法有一个限制条件,即要求图中不能包含该方法有一个限制条件,即要求图中不能包含有由带负权值的边组成的回路。有由带负权值的边组成的回路。

当图中没有由带负权值的边组成的回路时,有当图中没有由带负权值的边组成的回路时,有nn 个顶点的图中任意两个顶点之间如果存在最个顶点的图中任意两个顶点之间如果存在最短路径,此路径最多有短路径,此路径最多有 n-n-11 条边。条边。

我们将以此为依据考虑计算从我们将以此为依据考虑计算从源点源点 vv 到到其它顶其它顶点点 uu 的最短路径的长度的最短路径的长度 distdist[[uu]] 。。

0 1 2

5

7 -5 0 1 2

1 1

-2

Page 94: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

Bellman-FordBellman-Ford 方法构造一个最短路径长度数组序列方法构造一个最短路径长度数组序列 distdist1 1 [[uu]] ,,distdist2 2 [[uu]] ,…,,…, distdistnn-1 -1 [[uu]] 。其中,。其中, distdist1 1 [[uu]] 是从源点 是从源点 v v 到终点 到终点 u u 的的只经过一条边只经过一条边的最短路径的长度,的最短路径的长度, distdist1 1 [[uu] = ] = EdgeEdge[[vv][][uu]] ;;distdist2 2 [[uu]] 是从源点 是从源点 v v 最多经过两条边最多经过两条边到达终点 到达终点 uu 的最短路径的的最短路径的长度,长度, distdist3 3 [[uu]] 是从源点 是从源点 v v 出发出发最多经过不构成带负长度边回最多经过不构成带负长度边回路的三条边路的三条边到达终点 到达终点 u u 的最短路径的长度,…,的最短路径的长度,…, distdist nn-1-1[[uu]] 是是从源点 从源点 v v 出发最多经过不构成带负长度边回路的出发最多经过不构成带负长度边回路的

nn--11 条边条边到达终点 到达终点 u u 的最短路径的长度。的最短路径的长度。 算法的最终目的是计算出 算法的最终目的是计算出 distdist nn-1-1[[uu]] 。。 可以用递推方式计算 可以用递推方式计算 distdistk k [[uu]] 。。

Page 95: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

distdist1 1 [[uu] = ] = EdgeEdge[[vv][][uu]] ;; distdistk k [[uu] = min {] = min { dist distk-k-11 [[uu], min { ], min { distdistk-k-11 [[jj]+]+ EdgeEdge[[jj][][uu] } }] } }

设已经求出 设已经求出 distdistk-k-11 [[jj],], j j = 0, 1, …, = 0, 1, …, nn--11, , 此即从此即从源点 源点 v v 最多经过不构成带负长度边回路的 最多经过不构成带负长度边回路的 kk--1 1 条边到达终点 条边到达终点 j j 的最短路径的长度。的最短路径的长度。 从图的邻接矩阵中可以找到各个顶点 从图的邻接矩阵中可以找到各个顶点 j j 到达到达顶点 顶点 uu 的距离 的距离 EdgeEdge[[jj][][uu]] ,计算 ,计算 min { min { distdistk-k-11

[[jj]+]+ Edge Edge[[jj][][uu] }] } ,可得从源点 ,可得从源点 v v 绕过各个顶点,绕过各个顶点,最多经过不构成带负长度边回路的 最多经过不构成带负长度边回路的 k k 条边到达条边到达终点终点 uu 的最短路径的长度。的最短路径的长度。 用它与用它与 distdistk-k-11 [[uu]]比较,取小者作为比较,取小者作为 distdistk k [[uu]] 的值。的值。

Page 96: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

k d k[0] d k[1] d k[2] d k[3] d k[4] d k[5] dk[6] 1 0 6 5 5 2 0 3 3 5 5 4 3 0 1 3 5 2 4 7 4 0 1 3 5 0 4 5 5 0 1 3 5 0 4 3 6 0 1 3 5 0 4 3

图的最短路径长度图的最短路径长度

Page 97: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

计算最短路径的计算最短路径的 BellmanBellman 和和 FordFord 算法算法void Graph::BellmanFord ( int n, int v ) {//// 在带权有向图中有的边具有负的权值。从顶点 在带权有向图中有的边具有负的权值。从顶点 vv //// 找到所有其它顶点的最短路径。找到所有其它顶点的最短路径。 for ( int i = 0; i < n; i++ ) { dist[i] = Edge[v][i]; if ( i != v && dist[i] < MAXINT ) path[i] = v; else path[i] = -1; }

Page 98: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

for ( int k = 2; k < n; k++ ) for ( int u = 0; u < n; u++ )

if ( u != v ) for ( i = 0; i < n; i++ )

if ( Edge[i][u] > 0 && Edge[i][u] < MAXNUM && dist[u] > dist[i] + Edge[i][u] ) { dist[u] = dist[i] + Edge[i][u]; path[u] = i; }}

Page 99: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

所有顶点之间的最短路径所有顶点之间的最短路径 问题的提法:问题的提法:已知一个各边权值均大于已知一个各边权值均大于 00 的带的带权有向图,对每一对顶点 权有向图,对每一对顶点 vvii vvjj ,要求求出,要求求出 vv

ii 与与 vvjj 之间的最短路径和最短路径长度。之间的最短路径和最短路径长度。 FloydFloyd算法的基本思想:算法的基本思想: 定义一个定义一个 nn 阶方阵序列:阶方阵序列: AA(-1)(-1), , AA(0)(0), …, , …, AA((n-n-1)1).. 其中 其中 AA(-1) (-1) [[ii][][jj] = ] = EdgeEdge[[ii][][jj]] ;; AA((kk) ) [[ii][][jj] = min { ] = min { AA((kk-1)-1)[[ii][][jj]] ,, AA((kk-1)-1)[[ii][][kk] + ] + AA((kk-1)-1)[[kk][][jj] }, ] }, kk = 0,1,…, = 0,1,…, nn-1 -1

Page 100: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

AA(0) (0) [[ii][][jj]] 是从顶点是从顶点 vvi i 到到 vvj j , , 中间顶点是中间顶点是 vv00 的最短的最短路径的长度路径的长度 , , AA((kk) ) [[ii][][jj]] 是从顶点是从顶点 vvi i 到到 vvj j , , 中间顶中间顶点的序号不大于点的序号不大于 kk 的最短路径的长度的最短路径的长度 , , AA((n-n-1)1)[[ii][][jj]]是从顶点是从顶点 vvi i 到到 vvj j 的最短路径长度。的最短路径长度。

所有各对顶点之间的最短路径所有各对顶点之间的最短路径void Graph :: AllLengths ( int n ) {////EdgeEdge[[nn][][nn]] 是一个具有是一个具有 nn 个顶点的图的邻接矩阵个顶点的图的邻接矩阵。。 ////aa[[ii][][jj]] 是顶点 是顶点 ii 和和 jj 之间的最短路径长度。之间的最短路径长度。 ppathath[[ii][][jj]]//// 是相应路径上顶点是相应路径上顶点 j j 的前一顶点的顶点号的前一顶点的顶点号 , , 在在求求//// 最短路径时图的类定义中要修改最短路径时图的类定义中要修改 pathpath 为为////pathpath[[NumVerticesNumVertices][][NumVerticesNumVertices]] 。。

Page 101: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

for ( int i = 0; i < n; i++ ) // 矩阵 a 与 path初始化 for ( int j = 0; j < n; j++ ) { a[i][j] = Edge[i][j]; if ( i <> j && a[i][j] < MAXINT ) path[i][j] = i; // i 到 j 有路径

else path[i][j] = 0; // i 到 j 无路径 } for ( int k = 0; k < n; k++ ) //产生 a(k) 及 path(k) for ( i = 0; i < n; i++ )

for ( j = 0; j < n; j++ ) if ( a[i][k] + a[k][j] < a[i][j] ) { a[i][j] = a[i][k] + a[k][j];

path[i][j] = path[k][j]; } //缩短路径长度 , 绕过 k 到 j }

Page 102: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

A(-1) A(0) A(1) A(2) A(3)

0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 0 1 4 0 1 4 0 1 10 3 0 1 10 3 0 1 9 3 1 0 9 2 0 9 2 0 9 2 12 0 9 2 11 0 8 2 2 3 5 0 8 3 4 0 7 3 4 0 6 3 4 0 6 3 4 0 6 3 6 0 6 0 6 0 9 10 6 0 9 10 6 0

Path(-1) Path(0) Path(1) Path(2) Path(3)

0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 3 1 1 0 0 1 1 0 0 1 1 0 0 1 1 2 0 1 1 2 0 3 1 2 2 2 0 2 2 0 0 0 2 0 0 1 2 0 0 1 2 0 0 1 3 0 0 3 0 0 0 3 0 0 0 3 0 2 0 3 0 2 0 3 0

Page 103: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

以以 PathPath(3)(3) 为例,对最短路径的读法加以说明为例,对最短路径的读法加以说明。。从从 AA(3)(3) 知,顶点知,顶点 11 到到 00 的最短路径长度为的最短路径长度为 aa[1][0[1][0] = 11,] = 11,其最短路径看 其最短路径看 pathpath[1][[1][00] = ] = 22 ,, pathpath[1][[1][22] = ] = 33 ,, ppath ath [1][[1][33] = ] = 11, , 表示顶点表示顶点 0 0 顶点顶点 2 2 顶点顶点 3 3 顶点顶点 1;1; 从顶点从顶点 11 到顶点到顶点 00 最短路径为最短路径为 <1, 3>,<3, 2<1, 3>,<3, 2>,<2, 0>>,<2, 0> 。。 FloydFloyd算法允许图中有带负权值的边,但不许有算法允许图中有带负权值的边,但不许有包含带负权值的边组成的回路。包含带负权值的边组成的回路。 本章给出的求解最短路径的算法不仅适用于本章给出的求解最短路径的算法不仅适用于带权有向图,对带权无向图也可以适用。因为带带权有向图,对带权无向图也可以适用。因为带权无向图可以看作是有往返二重边的有向图,只权无向图可以看作是有往返二重边的有向图,只要在顶点要在顶点 vvi i 与与 vvj j 之间存在无向边之间存在无向边 ((vvi i , , vvj j )) ,就可以,就可以看成是在这两个顶点之间存在权值相同的两条有看成是在这两个顶点之间存在权值相同的两条有向边向边 << v vi i , , vvjj > > 和和 << v vj j , , vvii > > 。。

Page 104: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

活动网络 活动网络 (Activity Network)(Activity Network)

计划、施工过程、生产流程、程序流程等都是计划、施工过程、生产流程、程序流程等都是““工程工程”。除了很小的工程外,一般都把工程”。除了很小的工程外,一般都把工程分为若干个叫做“分为若干个叫做“活动活动”的子工程。完成了这”的子工程。完成了这些活动,这个工程就可以完成了。些活动,这个工程就可以完成了。 例如,计算机专业学生的学习就是一个工程,例如,计算机专业学生的学习就是一个工程,每一门课程的学习就是整个工程的一些活动。每一门课程的学习就是整个工程的一些活动。其中有些课程要求先修课程,有些则不要求。其中有些课程要求先修课程,有些则不要求。这样在有的课程之间有领先关系,有的课程可这样在有的课程之间有领先关系,有的课程可以并行地学习。以并行地学习。

用顶点表示活动的网络 用顶点表示活动的网络 (AOV(AOV 网络网络 ))

Page 105: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

CC11 高等数学高等数学 CC22 程序设计基础程序设计基础 CC33 离散数学 离散数学 CC11, C, C22 CC44 数据结构 数据结构 CC33, C, C22

CC55 高级语言程序设计 高级语言程序设计 CC22

CC66 编译方法 编译方法 CC55, C, C44

CC77 操作系统 操作系统 CC44, C, C99

CC88 普通物理 普通物理 CC11

CC99 计算机原理计算机原理 CC88

Page 106: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

学生课程学习工程图

Page 107: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

可以用可以用有向图有向图表示一个工程。在这种有向图中,表示一个工程。在这种有向图中,用顶点表示活动用顶点表示活动,,用有向边用有向边 <<VVii, , VVjj>> 表示活动。表示活动。VVi i 必须先于活动必须先于活动 VVjj 进行。这种有向图叫做顶点表进行。这种有向图叫做顶点表示活动的示活动的 AOVAOV 网络网络 (Activity On Vertices)(Activity On Vertices) 。 。

在在 AOVAOV 网络中,如果活动网络中,如果活动 VVi i 必须在活动必须在活动 VVj j 之前之前进行,则存在有向边进行,则存在有向边 <<VVii, , VVjj>> , , AOVAOV 网络中不网络中不能出现有向回路,即有向环。在能出现有向回路,即有向环。在 AOVAOV 网络中如网络中如果出现了有向环,则意味着某项活动应以自己作果出现了有向环,则意味着某项活动应以自己作为先决条件。为先决条件。

因此,对给定的因此,对给定的 AOVAOV 网络,必须先判断它是否网络,必须先判断它是否存在有向环。存在有向环。

Page 108: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

检测有向环的一种方法是对检测有向环的一种方法是对 AOVAOV 网络构造它的网络构造它的拓扑有序序列。即将各个顶点 拓扑有序序列。即将各个顶点 (( 代表各个活动代表各个活动 ))排列成一个线性有序的序列,使得排列成一个线性有序的序列,使得 AOVAOV 网络中网络中所有应存在的前驱和后继关系都能得到满足。 所有应存在的前驱和后继关系都能得到满足。 这种这种构造构造 AOVAOV 网络全部顶点的拓扑有序序列的网络全部顶点的拓扑有序序列的运算就叫做拓扑排序。运算就叫做拓扑排序。 如果通过拓扑排序能将如果通过拓扑排序能将 AOVAOV 网络的所有顶点都网络的所有顶点都排入一个拓扑有序的序列中,则该排入一个拓扑有序的序列中,则该 AOVAOV 网络中网络中必定不会出现有向环;相反,如果得不到满足要必定不会出现有向环;相反,如果得不到满足要求的拓扑有序序列,则说明求的拓扑有序序列,则说明 AOVAOV 网络中存在有网络中存在有向环,此向环,此 AOVAOV 网络所代表的工程是不可行的。网络所代表的工程是不可行的。

Page 109: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

例如,对学生选课工程图进行拓扑排序,得到的拓扑有序序列为例如,对学生选课工程图进行拓扑排序,得到的拓扑有序序列为 CC11 , C , C22 , C , C33 , C , C44 , C , C55 , C , C66 , C , C88 , C , C99 , C , C77或 或 CC11 , C , C88 , C , C99 , C , C2 2 , C, C55 , C , C3 3 , C, C44 , C , C77 , C , C66

Page 110: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

进行拓扑排序的方法进行拓扑排序的方法 输入输入 AOVAOV 网络。令 网络。令 n n 为顶点个数。为顶点个数。 在在 AOVAOV 网络中选一个没有直接前驱的顶点网络中选一个没有直接前驱的顶点 , , 并输出之并输出之 ; ; 从图中删去该顶点从图中删去该顶点 , , 同时删去所有它发出的同时删去所有它发出的有向边有向边 ;; 重复以上 重复以上 、、 步步 , , 直到直到

全部顶点均已输出,拓扑有序序列形成,拓扑全部顶点均已输出,拓扑有序序列形成,拓扑排序完成;或排序完成;或 图中还有未输出的顶点,但已跳出处理循环。图中还有未输出的顶点,但已跳出处理循环。这说明图中还剩下一些顶点,它们都有直接前这说明图中还剩下一些顶点,它们都有直接前驱,再也找不到没有前驱的顶点了。这时驱,再也找不到没有前驱的顶点了。这时 AOVAOV网络中必定存在有向环。网络中必定存在有向环。

Page 111: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

C0 C1 C2

C3 C4 C5

拓扑排序的过程拓扑排序的过程

(a) 有向无环图

C2

C5

C1C0

C3

(b) 输出顶点 C4

C1 C2

C5C3

(c) 输出顶点 C0

C4

C0 C2

C5

C1

C3

(d) 输出顶点 C3

Page 112: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

C1 C2

C5

(e) 输出顶点 C2

C5

C1

(f) 输出顶点 C1

C5

(g) 输出顶点 C5

最后得到的拓扑有序序列为 最后得到的拓扑有序序列为 CC44 , C , C00 , C , C33 , C , C

22 , C , C1 1 , C, C55 。它满足图中给出的所有前驱和后继。它满足图中给出的所有前驱和后继关系,对于本来没有这种关系的顶点,如关系,对于本来没有这种关系的顶点,如 CC44 和和CC22 ,也排出了先后次序关系。,也排出了先后次序关系。

(h) 拓扑排序完成

Page 113: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

AOVAOV 网络及其邻接表表示网络及其邻接表表示

C0 C1 C2

C3 C4 C5

C0

C1

C2

C3 0

C4

C5 0

0

1

2

3

4

5

count data adj

1

3

0

1

0

3

1 dest link

3 0

5

1 5 0

0 1 5 0

Page 114: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在邻接表中增设了一个数组在邻接表中增设了一个数组 countcount[[ ]] ,记录各个,记录各个顶点入度。入度为零的顶点即无前驱的顶点。顶点入度。入度为零的顶点即无前驱的顶点。 在输入数据前,邻接表的顶点表在输入数据前,邻接表的顶点表 NodeTableNodeTable[ ][ ] 和和入度数组入度数组 countcount[ ][ ] 全部初始化。在输入数据时,全部初始化。在输入数据时,每输入一条边每输入一条边 <<jj, , k>k> ,就需要建立一个边结点,,就需要建立一个边结点,并将它链入相应边链表中,统计入度信息:并将它链入相应边链表中,统计入度信息:

EdgeEdge<int> * <int> * p p = = newnew EdgeEdge<int>(<int>(kk);); //// 建立边建立边结点结点 ,, dest dest 域赋为 域赋为 kk pp→→linklink = = NodeTableNodeTable[[jj].].adjadj;; NodeTableNodeTable[[jj]].adj.adj = = pp;; //// 链入顶点 链入顶点 j j 的边链表的前端的边链表的前端 countcount[[kk]++;]++; //// 顶点 顶点 kk 入度加一 入度加一

Page 115: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在拓扑排序算法中,使用一个存放入度为零的在拓扑排序算法中,使用一个存放入度为零的顶点的链式栈,供选择和输出无前驱的顶点。顶点的链式栈,供选择和输出无前驱的顶点。只要出现入度为零的顶点,就将它加入栈中。只要出现入度为零的顶点,就将它加入栈中。 使用这种栈的拓扑排序算法可以描述如下:使用这种栈的拓扑排序算法可以描述如下: (1) (1) 建立入度为零的顶点栈建立入度为零的顶点栈 ;; (2) (2) 当入度为零的顶点栈不空时当入度为零的顶点栈不空时 , , 重复执行 重复执行

从顶点栈中退出一个顶点从顶点栈中退出一个顶点 , , 并输出之并输出之 ;; 从从 AOVAOV 网络中删去这个顶点和它发出的边网络中删去这个顶点和它发出的边 ,,

边的终顶点入度减一边的终顶点入度减一 ;; 如果边的终顶点入度减至如果边的终顶点入度减至 0, 0, 则该顶点进则该顶点进入度为零的顶点栈入度为零的顶点栈 ;;

(3) (3) 如果输出顶点个数少于如果输出顶点个数少于 AOVAOV 网络的顶点个网络的顶点个数数 , , 则报告网络中存在有向环。则报告网络中存在有向环。

Page 116: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在算法实现时,为了建立入度为零的顶点栈,可以在算法实现时,为了建立入度为零的顶点栈,可以不另外分配存储空间,直接利用入度为零的顶点的不另外分配存储空间,直接利用入度为零的顶点的countcount[ ][ ] 数组元素。我们设立了一个栈顶指针数组元素。我们设立了一个栈顶指针 toptop ,,指示当前栈顶的位置,即某一个入度为零的顶点位指示当前栈顶的位置,即某一个入度为零的顶点位置。栈初始化时置置。栈初始化时置 top top = -1= -1 ,表示空栈。,表示空栈。

将顶点 将顶点 I I 进栈时,执行以下指针的修改:进栈时,执行以下指针的修改: countcount[[ii] = ] = toptop; ; top = itop = i;; //// toptop 指向新栈顶指向新栈顶 ii, , 原栈顶元素放在原栈顶元素放在 countcount[[ii]] 中中

退栈操作可以写成:退栈操作可以写成: j j = = toptop; ; toptop = = countcount[[toptop];]; //// 位于栈顶的顶点的位置记于 位于栈顶的顶点的位置记于 jj, , toptop退到次栈顶退到次栈顶

Page 117: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

拓扑排序时入拓扑排序时入度为零的顶点度为零的顶点栈的变化栈的变化

Page 118: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

class Graph { // 图的邻接表的类定义图的邻接表的类定义friend class <int, float>Vertex;friend class <float>Edge;private: Vertex<int, float> *NodeTable; int *count; int n;public: Graph ( const int vertices = 0 ) : n ( vertices ) { NodeTable = new vertex<int, float>[n]; count = new int[n]; }; void TopologicalOrder ( );}

Page 119: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

拓扑排序的算法拓扑排序的算法void Graph :: TopologicalSort ( ) { int top = -1; // 入度为零的顶点栈初始化 for ( int i = 0; i < n; i++ ) // 入度为零顶点进栈 if ( count[i] == 0 ) { count[i] = top; top = i; } for ( i = 0; i < n; i++ ) //期望输出 n 个顶点 if ( top == -1 ){ // 中途栈空 ,转出 cout << “ 网络中有回路 ( 有向环 )" << endl;

return; } else { //继续拓扑排序

int j = top; top = count[top]; //退栈

Page 120: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

cout << j << endl; // 输出 Edge<float> * l = NodeTable[j].adj; while ( l ) { //扫描该顶点的出边表

int k = l→dest; // 另一顶点 if ( --count[k] == 0 ) // 该顶点入度减一

{ count[k] = top; top = k; } //减至零 l = l→link; } }}

Page 121: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

分析此拓扑排序算法可知,如果分析此拓扑排序算法可知,如果 AOVAOV 网络有网络有n n 个顶点,个顶点, e e 条边,在拓扑排序的过程中,条边,在拓扑排序的过程中,搜索入度为零的顶点,建立链式栈所需要的搜索入度为零的顶点,建立链式栈所需要的时间是时间是 O(O(nn)) 。在正常的情况下,有向图有 。在正常的情况下,有向图有 n n 个顶点,每个顶点进一次栈,出一次栈,共个顶点,每个顶点进一次栈,出一次栈,共输出 输出 n n 次。顶点入度减一的运算共执行了 次。顶点入度减一的运算共执行了 e e 次。所以总的时间复杂度为次。所以总的时间复杂度为 O(O(nn++ee)) 。。

Page 122: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

用边表示活动的网络用边表示活动的网络 (AOE(AOE 网络网络 )) 如果在如果在无有向环的带权有向图无有向环的带权有向图中中

用有向边表示一个工程中的活动用有向边表示一个工程中的活动 (Activity)(Activity) 用边上权值表示活动持续时间用边上权值表示活动持续时间 (Duration)(Duration) 用顶点表示事件 用顶点表示事件 (Event)(Event)

则这样的有向图叫做用边表示活动的网络,简则这样的有向图叫做用边表示活动的网络,简称 称 AOE (Activity On Edges) AOE (Activity On Edges) 网络。网络。 AOEAOE 网络在某些工程估算方面非常有用。例网络在某些工程估算方面非常有用。例如,可以使人们了解:如,可以使人们了解: (1) (1) 完成整个工程至少需要多少时间完成整个工程至少需要多少时间 ((假设网假设网络中没有环络中没有环 )? )?

Page 123: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

(2) (2) 为缩短完成工程所需的时间为缩短完成工程所需的时间 , , 应当加快哪些活应当加快哪些活动动 ? ? 在在 AOEAOE 网络中网络中 , , 有些活动有些活动顺序进行顺序进行,有些活动,有些活动并并行进行行进行。。 从源点到各个顶点,以至从源点到汇点的有向路从源点到各个顶点,以至从源点到汇点的有向路径可能不止一条。这些路径的长度也可能不同。径可能不止一条。这些路径的长度也可能不同。完成不同路径的活动所需的时间虽然不同,但只完成不同路径的活动所需的时间虽然不同,但只有各条路径上所有活动都完成了,整个工程才算有各条路径上所有活动都完成了,整个工程才算完成。完成。 因此,因此,完成整个工程所需的时间取决于从源点到完成整个工程所需的时间取决于从源点到汇点的最长路径长度汇点的最长路径长度,即在这条路径上所有活动,即在这条路径上所有活动的持续时间之和。的持续时间之和。这条路径长度最长的路径就叫这条路径长度最长的路径就叫做关键路径做关键路径 (Critical Path)(Critical Path) 。。

Page 124: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

1

3

2

4a1=8

a2=12

5

6

7 8a10=12

a9=6

a8=18

a5=28

a6=8

a7=6a3=14a4=10

要找出关键路径,必须找出要找出关键路径,必须找出关键活动关键活动,即不按期完,即不按期完成就会影响整个工程完成的活动。成就会影响整个工程完成的活动。 关键路径上的所有活动都是关键活动关键路径上的所有活动都是关键活动。因此,只要。因此,只要找到了关键活动,就可以找到关键路径找到了关键活动,就可以找到关键路径 .. 例如,下图就是一个例如,下图就是一个 AOEAOE 网。网。

Page 125: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

定义几个与计算关键活动有关的量:定义几个与计算关键活动有关的量: 事件事件 VVii 的最早可能开始时间的最早可能开始时间 VeVe((ii) ) 是从是从源点源点 VV00 到到顶点顶点 VVi i 的最长路径长度。的最长路径长度。 事件事件 VVi i 的最迟允许开始时间的最迟允许开始时间 VlVl[[ii]] 是在保证是在保证汇点汇点 VVnn-1-1 在在 VeVe[[nn-1] -1] 时刻完成的前提时刻完成的前提下,事件下,事件 VVii 的允许的最迟开始时间。的允许的最迟开始时间。

活动活动 aak k 的最早可能开始时间 的最早可能开始时间 ee[[kk]] 设设活动活动 aak k 在在边边 << V Vi i , , VVjj > > 上,则上,则 ee[[kk]] 是从源点是从源点 VV

00 到顶点到顶点 VVii 的最长路径长度。因此的最长路径长度。因此 , , ee[[kk] = ] = VeVe[[ii]] 。。 活动活动 aak k 的最迟允许开始时间 的最迟允许开始时间 ll[[kk]] ll[[kk]] 是在不会引起时间延误的前提下,该活动允是在不会引起时间延误的前提下,该活动允许的最迟开始时间。 许的最迟开始时间。

Page 126: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

ll[[kk] =] = Vl Vl[[jj] - ] - durdur(<(<ii, , jj>)>) 。。 其中,其中, durdur(<(<ii, , jj>)>) 是完成 是完成 aak k 所需的时间。 所需的时间。 时间余量 时间余量 ll[[kk] - ] - ee[[kk]] 表示活动 表示活动 aak k 的最早可能开始时间和最迟允许开的最早可能开始时间和最迟允许开始时间的时间余量。始时间的时间余量。 ll[[kk] ] == e== e[[kk] ] 表示活动 表示活动 aak k 是没是没有时间余量的关键活动。有时间余量的关键活动。

为找出关键活动为找出关键活动 , , 需要求各个活动的 需要求各个活动的 ee[[kk] ] 与 与 ll[[kk]] ,,以判别是否 以判别是否 ll[[kk] ] == e== e[[kk].]. 为求得 为求得 ee[[kk]] 与 与 ll[[kk]] ,需要先求得从源点 ,需要先求得从源点 VV0 0 到各到各个顶点 个顶点 VVi i 的 的 VeVe[[ii] ] 和 和 VlVl[[ii]] 。。

求求 VeVe[[ii]] 的递推公式的递推公式

Page 127: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

从 从 VeVe[0] = 0[0] = 0开始,向前递推开始,向前递推 < < VVjj, , VVi i > > SS2, 2, ii = 1, 2, = 1, 2, , , nn-1-1

其中其中 , , SS22 是所有指向顶点是所有指向顶点 VVi i 的有向边的有向边 << V Vjj , , VVii >> 的的集合。集合。 从从 VlVl[[nn-1] = -1] = VeVe[[nn-1]-1]开始,反向递推开始,反向递推 << V Vii, , VVjj > > SS1, 1, ii = = nn-2, -2, nn-3, -3, , 0, 0

其中其中 , , SS11 是所有从顶点是所有从顶点 VVii 发出的有向边发出的有向边 << V Vii , , VVjj >>的集合。的集合。 这两个递推公式的计算必须分别在拓扑有序及逆拓这两个递推公式的计算必须分别在拓扑有序及逆拓扑有序的前提下进行。扑有序的前提下进行。

}, ) ,(][ {][ ijjVVdurjVemaxiVe

}, ) ,(][ {][ jijVVdurjVlminiVl

Page 128: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

设活动设活动 aak k ((kk = 1, 2, …, = 1, 2, …, ee)) 在带权有向边在带权有向边 << V Vii , , VVjj > > 上上 , , 它的持续时间用它的持续时间用 dur dur (<(<VVii , , VVjj >) >) 表示,则有表示,则有

ee[[kk] =] = Ve Ve[[ii]] ;; ll[[kk] =] = Vl Vl[[jj] - ] - durdur(<(<VVii , , VVjj >)>) ;; kk = 1, 2, …, = 1, 2, …, ee 。。这这样就得到计算关键路径的算法。样就得到计算关键路径的算法。

计算关键路径时,可以一边进行拓扑排序一边计计算关键路径时,可以一边进行拓扑排序一边计算各顶点的算各顶点的 VeVe[[ii]] 。为了简化算法,假定在求关键。为了简化算法,假定在求关键路径之前已经对各顶点实现了拓扑排序,并按拓路径之前已经对各顶点实现了拓扑排序,并按拓扑有序的顺序对各顶点重新进行了编号。算法在扑有序的顺序对各顶点重新进行了编号。算法在求求 VeVe[[ii], ], ii=0, 1, …, =0, 1, …, nn-1-1 时按拓扑有序的顺序计算,时按拓扑有序的顺序计算,在求在求 VlVl[[ii], ], ii==nn-1, -1, nn-2, …, 0-2, …, 0 时按逆拓扑有序的顺序时按逆拓扑有序的顺序计算。计算。

Page 129: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

1

3

2

4

a1=8

a2=12

5

6

7 8a10=12

a9=6

a8=18

a5=28

a6=8

a7=6a3=14a4=10

VeVl

1 2 3 4 5 6 7 8 0 8 12 22 28 40 46 58 0 8 12 22 28 40 46 58e

l0 0 8 12 12 22 22 28 40 460 0 8 12 12 32 22 28 40 46

1 2 3 4 5 6 7 8 9 10

Page 130: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

利用关键路径法求利用关键路径法求 AOEAOE 网的各关键活动网的各关键活动void graph :: CriticalPath ( ) {// 在此算法中需要对邻接表中单链表的结点加以//修改 , 在各结点中增加一个 int 域 cost, 记录该结// 点所表示的边上的权值。 int i, j; int p, k; float e, l; float * Ve = new float[n]; float * Vl = new float[n]; for ( i = 0; i < n; i++ ) Ve[i] = 0; for ( i = 0; i < n; i++ ) { Edge<float> *p = NodeTable[i].adj; while ( p != NULL ) {

k = p→dest;

Page 131: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

if ( Ve[i] + p→cost > Ve[k] )   Ve[k] = Ve[i] + p→cost; p = p→link;

} } for ( i = 0; i < n; i++ ) Vl[i] = Ve[n-1]; for ( i = n-2; i; i-- ) { p = NodeTable[i].adj; while ( p != NULL ) { k = p→dest; if ( Vl[k] - p→cost < Vl[i])

Vl[i] = Vl[k] - p→cost; p = p→link;

Page 132: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

} } for ( i = 0; i < n; i++ ) { p = NodeTable[i].adj; while ( p != NULL ) {

k = p→dest; e = Ve[i]; l = Vl[k] - p→cost; if ( l == e ) cout << "<" << i << "," << k

<< “>” << “ 是关键活动” << endl; p = p→link;

} }}

Page 133: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

在拓扑排序求 在拓扑排序求 VeVe[[ii] ] 和逆拓扑有序求 和逆拓扑有序求 VlVl[[ii] ] 时时 , , 所需时间为所需时间为 O(O(nn++ee), ), 求各个活动的 求各个活动的 ee[[kk]]和 和 ll[[kk]] 时所需时间为时所需时间为 O(O(ee), ), 总共花费时间仍然总共花费时间仍然是是 O(O(nn++ee)) 。。 注意注意

所有顶点按拓扑有序的次序编号所有顶点按拓扑有序的次序编号 仅计算 仅计算 VeVe[[ii] ] 和 和 VlVl[[ii]] 是不够的,还须计是不够的,还须计算 算 ee[[kk]] 和 和 ll[[kk]] 。。 不是任一关键活动加速一定能使整个工程不是任一关键活动加速一定能使整个工程提前。提前。 想使整个工程提前,要考虑各个关键路径想使整个工程提前,要考虑各个关键路径上所有关键活动。上所有关键活动。

Page 134: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

小结 需要复习的知识小结 需要复习的知识点点 理解:图的基本概念理解:图的基本概念

图的顶点集合是非空有限集合图的顶点集合是非空有限集合 图的边集合可以是空集合图的边集合可以是空集合 图的顶点序号不是固有的图的顶点序号不是固有的

掌握:图的存储表示掌握:图的存储表示 图的邻接矩阵元素个数与顶点有图的邻接矩阵元素个数与顶点有关,非零元素个数与边有关关,非零元素个数与边有关 图的邻接表既与顶点个数 图的邻接表既与顶点个数 n n 有关,有关,又与边数有关又与边数有关

Page 135: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

连通无向图最多有 连通无向图最多有 nn((nn--1)/2 1)/2 条边,条边,最少有最少有 nn--11 条边。条边。 强连通有向图最多有 强连通有向图最多有 nn((nn--1) 1) 条边,条边,最少有 最少有 nn 条边。条边。 n n = 1= 1 无边。无边。 图中顶点的度有别于树中结点的度。图中顶点的度有别于树中结点的度。

图的遍历与连通性图的遍历与连通性 深度优先搜索算法深度优先搜索算法 递归或用栈 广度优先搜索算法广度优先搜索算法 用队列 生成树或生成森林生成树或生成森林

Page 136: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

连通分量连通分量 重连通分量重连通分量与与关节点关节点的概念的概念 求解求解关节点关节点及构造及构造重连通图重连通图的方法的方法

掌握 掌握 : : 构造最小生成树的方法构造最小生成树的方法 Prim Prim 算法算法 Kruskal Kruskal 算法算法

掌握 掌握 : : 活动网络的拓扑排序算法活动网络的拓扑排序算法 掌握 掌握 : : 求解关键路径的方法求解关键路径的方法 理解 理解 : : 求解最短路径的求解最短路径的 DijkstraDijkstra 方法方法

(( 不要求算法不要求算法 ))

Page 137: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

【例例 11 】】以深度优先搜索方法从 出发以深度优先搜索方法从 出发遍历图遍历图 , , 建立深度优先生成森林。建立深度优先生成森林。A B C

D E F

G

A

A

B

DE

C

F

G有向图有向图 深度优先生成树深度优先生成树

Page 138: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

template<class Type> void Graph<Type> ::DFS_Forest ( Tree<Type> &T ) { TreeNode<Type> *rt, *subT; int *visited = new int[n]; // 创建访问标志数组 for ( int i = 0; i < n; i++ ) visited [i] = 0; //初始化 ,都未访问过 for ( i = 0; i < n; i++ ) // 遍历所有顶点 if ( !visited[i] ) { // 顶点 i 未访问过

if ( T.IsEmpty ( ) ) //原为空森林 , 建根 subT = rt = T.BuildRoot ( GetValue(i) ); // 顶点 i 的值成为根 rt 的值

Page 139: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

else subT = T.InsertRightSibling ( subT, GetValue(i) ); // 顶点 i 的值成为 subT 右兄弟的值 DFS_Tree ( T, subT, i, visited ); // 从顶点 i 出发深度优先遍历 // 建立以 subT 为根的 T 的子树

}}

template<class Type> void Graph<Type> :: DFS_Tree ( Tree<Type> &T, TreeNode<Type>*RT, int i, int visited [ ] ) {

Page 140: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

TreeNode<Type> *p; visited [i] = 1; // 顶点 i 作访问过标志 int w = First-Adjvertex(i); // 取顶点 i 的第一个邻接顶点 w int FirstChild = 1; // i 第一个未访问子女应是 i 的左子女 while ( w ) { // 邻接顶点 w 存在 if ( ! visited [w] ) { // w未访问过 , 将成为 i 的子女

if ( FirstChild ) { p = T.InsertLeftChild ( RT, GetValue(w) ); // p 插入为 RT 的左子女

Page 141: 图的基本概念 图的存储表示 图的遍历与连通性 最小生成树 最短路径 活动网络 小结

FirstChild = 0; // 建右兄弟 } else p = T.InsertRightSibling ( p, GetValue(w) ); // p 插入为 p 的左子女 DFS_Tree ( T, p, w ); //递归建立 w 的以 p 为根的子树

} // 邻接顶点 w 处理完 w = Next-AdjVertex ( i, w ); // 取 i 的下一个邻接顶点 w } // 回到 while 判邻接顶点 w 存在}