1、n图的基本概念图的基本概念n图的存储表示图的存储表示n图的遍历与连通性图的遍历与连通性 n最小生成树最小生成树n最短路径最短路径 n活动网络活动网络 (vertex): 。class Graph public: Graph ( ); void InsertVertex ( const Type & vertex ); void InsertEdge ( const int v1, const int v2, int weight ); void RemoveVertex ( const int v ); void RemoveEdge ( const int v1, const int v2
2、); int IsEmpty ( ); Type GetWeight ( const int v1, const int v2 ); int GetFirstNeighbor ( const int v ); int GetNextNeighbor ( const int v1, const int v2 ); , ),( , , .否则否则如果如果01AEjiEjijiEdge或者 ),( , ,),( .jijiEjiEjijijiWjiEdge=! !0=A对角线但是否则,或且如果template class Graph private: SeqList VerticesList (Ma
3、xVertices); DistType EdgeMaxVerticesMaxVertices; int numVertex, numEdge; int FindVertex ( SeqList & L; const NameType &vertex ) return L.Find (vertex); int GetVertexPos ( Const NameType &vertex ) return FindVertex (VerticesList, vertex ); public: Graph ( const int sz=MaxNumEdges ); int GraphEmpty (
4、)const return VerticesList.IsEmpty ( ); int GraphFull( ) const return VerticesList.IsFull( ) | numEdges =MaxEdges; int NumberOfVertices ( ) return numVertex; int NumberOfEdges ( ) return numEdges; NameType GetValue ( const int i ) return i = 0 & i VerticesList.length-1 ? VerticesList.Elemi : NULL; i
5、nt GetWeight ( const int v1, const int v2 ); int GetFirstNeighbor ( const int v ); int GetNextNeighbor ( const int v1, const int v2 ); void InsertVertex ( const NameType & vertex ); void InsertEdge ( const int v1, const int v2, DistType weight ); void RemoveVertex ( const int v ); void RemoveEdge (
6、const int v1, const int v2 );template Graph:Graph(const int sz)/构造函数构造函数 for ( int i = 0; i sz; i+ ) for ( int j = 0; j sz; j+ ) Edgeij = 0; numVertex=numEdges = 0;template DistType Graph:GetWeight( const int v1, const int v2 ) /给出以顶点给出以顶点 v1 和和 v2 为两端点的边上的权值为两端点的边上的权值 if ( v1 != -1 & v2 != -1 ) ret
7、urn Edgev1v2; else return 0;template int Graph:GetFirstNeighbor ( const int v ) /给出顶点位置为给出顶点位置为 v 的第一个邻接顶点的位置的第一个邻接顶点的位置 if ( v != -1 ) for ( int col = 0; col 0 ) return col; return -1; template int Graph:GetNextNeighbor ( const int v1, const int v2 ) /给出顶点给出顶点v1的某邻接顶点的某邻接顶点v2的下一个邻接顶点的下一个邻接顶点 if ( v
8、1 != -1 & v2 != -1 ) for ( int col = v2+1; col 0 ) return col; return -1; template class Graph;template struct Edge int dest; DistType cost; Edge *link; Edge ( ) Edge ( int D, DistType C ) : dest (D), cost (C), link (NULL) int operator != ( const Edge &E ) const return dest != E.dest; template struc
9、t Vertex NameType data; Edge *adj;template class Graph friend class vertex ;friend class Edge;private: Vertex *NodeTable; int NumVertices; int MaxVertices; int NumEdges; int GetVertexPos ( const Type & vertex );public: Graph ( int sz ); Graph ( ); int GraphEmpty ( ) const return NumVertices = 0; int
10、 GraphFull ( ) const return NumVertices = MaxVertices | NumEdges =MaxEdges; NameType GetValue ( const int i ) return i = 0 & i NumVertices ? NodeTablei.data : NULL; int NumberOfVertices ( ) return NumVertices; int NumberOfEdges ( ) return NumEdges; void InsertVertex ( const NameType & vertex ); void
11、 RemoveVertex ( const int v ); void InsertEdge ( const int v1, const int v2, const DistType weight ); void RemoveEdge ( const int v1, const int v2 ); DistType GetWeight ( const int v1, const int v2 ); int GetFirstNeighbor ( const int v ); int GetNextNeighbor ( const int v1, const int v2 );template G
12、raph:Graph ( const int sz = DefaultSize ) : NumVertices (0), MaxVertices (sz), NumEdges (0) int n, e, k, j; NameType name, tail, head; DistType weight; NodeTable = /创建顶点表创建顶点表 new VertexMaxVertices; cin n; /输入顶点个数输入顶点个数 for ( int i = 0; i name; InserVertex ( name ); cin e; /输入边条数输入边条数 for ( i =0; i
13、tail head weight; k = GetVertexPos ( tail ); j = GetVertexPos ( head ); InsertEdge ( k, j, weight ); template Graph:Graph ( ) for ( int i = 0; i NumVertices; i+ ) Edge *p = NodeTablei.adj; while ( p != NULL ) /逐条边释放逐条边释放 NodeTablei.adj = plink; delete p; p = NodeTablei.adj; delete NodeTable; /释放顶点表释
14、放顶点表template int Graph:GetVertexPos ( Const NameType & vertex ) /根据顶点名根据顶点名vertex查找该顶点在邻接表中的位置查找该顶点在邻接表中的位置 for ( int i =0; i NumVertices; i+ ) if ( NodeTablei.data = vertex ) return i; return -1;template int Graph:GetFirstNeighbor ( const int v ) /查找顶点查找顶点 v 的第一个邻接顶点在邻接表中的位置的第一个邻接顶点在邻接表中的位置 if ( v
15、!= -1 ) /若顶点存在若顶点存在 Edge *p = NodeTablev.adj; if ( p != NULL ) return pdest; return -1; /若顶点不存在若顶点不存在template int Graph: GetNextNeighbor ( const int v1, const int v2 ) /查找顶点查找顶点 v1 在邻接顶点在邻接顶点 v2 后下一个邻接顶点后下一个邻接顶点 if ( v1 != -1 ) Edge *p = NodeTablev1.adj; while ( p != NULL ) if ( pdest = v2 & plink !
16、= NULL ) return plinkdest; /返回下一个邻接顶点在邻接表中的位置返回下一个邻接顶点在邻接表中的位置 else p = plink; return -1; /没有查到下一个邻接顶点返回没有查到下一个邻接顶点返回- -1template DistType Graph:GetWeight ( const int v1, const int v2) /取两端点为取两端点为 v1 和和 v2 的边上的权值的边上的权值 if ( v1 != -1 & v2 != -1 ) Edge *p = NodeTablev1.adj; while ( p != NULL ) if ( pd
17、est = v2 ) return pcost; else p = plink; return 0; mark vertex1 vertex2 path1 path2 data Firstout mark vertex1 vertex2 path1 path2 data Firstin Firstoutvoid Graph:DFS ( ) visited = new int n; /创建数组创建数组 visited for ( int i = 0; i n; i+ ) visited i = 0; /访问标记数组访问标记数组 visited 初始化初始化 for(int i=0; in;i+)
18、if(visitedi=0) DFS(i,visited); delete visited; /释放释放 visited viod Graph:DFS ( const int v, int visited ) cout GetValue (v) ; /访问顶点访问顶点 v visitedv = 1; /顶点顶点 v 作访问标记作访问标记 int w = GetFirstNeighbor (v); /取取 v 的第一个邻接顶点的第一个邻接顶点 w while ( w != -1 ) /若邻接顶点若邻接顶点 w 存在存在 if ( !visitedw ) DFS ( w, visited ); /
19、若若顶点顶点 w 未访问过未访问过, , 递归访问顶点递归访问顶点 w w = GetNextNeighbor ( v, w ); /取顶点取顶点 v 的排在的排在 w 后面的下一个邻接顶点后面的下一个邻接顶点 void Graph : BFS ( const int v ) visited = new intNumCertices; /创建创建 visited for ( int i = 0; i NumVertices; i+ ) visitedi = 0; /visited 初始化初始化 cout GetValue (v) ; visitedv = 1; Queue q; q.EnQue
20、ue (v); /访问访问 v, 进队列进队列 while ( !q.IsEmpty ( ) ) /队空搜索结束队空搜索结束 v = q.DeQueue ( ); /不空不空, 出队列出队列 int w = GetFirstNeighbor (v); /取顶点取顶点 v 的的第一个邻接顶点第一个邻接顶点 w while ( w != -1 ) /若邻接顶点若邻接顶点 w 存在存在 if ( !visitedw ) /若该邻接顶点未访问过若该邻接顶点未访问过 cout GetValue (w) ; /访问访问 visitedw = 1; q.EnQueue (w); /进队进队 w = GetN
21、extNeighbor (v, w); /取顶点取顶点 v 的排在的排在 w 后面的下一邻接顶点后面的下一邻接顶点 /重复检测重复检测 v 的所有邻接顶点的所有邻接顶点 delete visited; void Graph:Components ( ) visited = new intNumCertices; /创建创建visited for ( int i = 0; i NumVertices; i+ ) visitedi = 0; /visited 初始化初始化 for ( i = 0; i NumVertices; i+ ) if ( !visitedi ) /检测所有顶点是否访问过检
22、测所有顶点是否访问过 DFS ( i, visited ); /从未访问的顶点出发访问从未访问的顶点出发访问 OutputNewComponent ( ); /输出一个连通分量输出一个连通分量 delete visited; /释放释放visitedlowu = min dfnu, min loww | w 是是 u 的一个子女的一个子女 , min dfnx | (u, x) 是一条回边是一条回边 计算计算dfn与与low的算法的算法 (1)void Graph:DfnLow ( const int x ) /公有函数:从顶点公有函数:从顶点x开始深度优先搜索开始深度优先搜索 int num
23、 = 1; / num是访问计数器是访问计数器 dfn = new intNumVertices; low = new intNumVertices; /dfn是深度优先数是深度优先数, low是最小祖先访问顺序号是最小祖先访问顺序号 for ( int i = 0; i NumVertices; i+ ) dfni = lowi = 0; /给予访问计数器给予访问计数器num及及dfnu, lowu初值初值 DfnLow ( x, -1 ); /从根从根x开始开始 delete dfn; delete low;计算计算dfn与与low的算法的算法 (2)void Graph:DfnLow (
24、 const int u, const int v ) /私有函数:从顶点私有函数:从顶点 u 开始深度优先搜索计算开始深度优先搜索计算dfn/ 和和low。在产生的生成树中。在产生的生成树中 v 是是 u 的双亲。的双亲。 dfnu = lowu = num+; int w = GetFirstNeighbor (u); while ( w != -1 ) /对对u所有邻接顶点所有邻接顶点w循环循环 if ( dfnw = 0 ) /未访问过未访问过, w是是u的孩子的孩子 DfnLow ( w, u ); /从从w递归深度优先搜索递归深度优先搜索 lowu = min2 ( lowu, l
25、oww ); /子女子女w的的loww先求出先求出, 再求再求lowu else if ( w != v ) /w访问过且访问过且w不是不是v,是回边是回边 lowu = min2 ( lowu, dfnw ); /根据回边另一顶点根据回边另一顶点w调整调整lowu w = GetNextNeighbor (u, w); /找顶点找顶点u在在w后面后面的下一个邻接顶点的下一个邻接顶点 void Graph:Biconnected ( ) /公有函数:从顶点公有函数:从顶点0开始深度优先搜索开始深度优先搜索 int num = 1; /访问计数器访问计数器num dfn = new intNum
26、Vertices; /dfn是深度优先数是深度优先数 low = new intNumVertices; /low是最小祖先号是最小祖先号 for ( int i = 0; i NumVertices; i+ ) dfni = lowi = 0; DfnLow ( 0, -1 ); /从顶点从顶点 0 开始开始 delete dfn; delete low;void Graph:Biconnected ( const int u, const int v ) /私有函数:计算私有函数:计算dfn与与low, 根据其重连通分量输根据其重连通分量输/出出Graph的边。在产生的生成树中的边。在产生
27、的生成树中, v 是是 u 的双亲的双亲/结点结点, S 是一个初始为空的栈,应声明为图的数是一个初始为空的栈,应声明为图的数/据成员。据成员。 int x, y, w; dfnu = lowu = num+; w = GetFirstNeighbor (u); /找顶点找顶点u的第一个邻接顶点的第一个邻接顶点w while ( w != - 1 ) if ( v != w & dfnw = dfnu ) /无回边无回边, 原来的重连通分量结束原来的重连通分量结束 cout “新重连通分量新重连通分量: ” end1; do (x, y) = S.Pop ( ); cout x , y end
28、l; while ( (x, y) 与与 (u, w) 不是同一条边不是同一条边 ); /输出该重连通分量的各边输出该重连通分量的各边 else if ( w != v ) /有回边有回边, ,计算计算 lowu = min2 ( lowu, dfnw ); /根据回边另一顶点根据回边另一顶点w调整调整lowu w = GetNextNeighbor (u, w); /找顶点找顶点u的邻接顶点的邻接顶点w的下一个邻接顶点的下一个邻接顶点 471592(a) (b) (c) (d)n最小生成树最小生成树:图图G中中 最小权值最小权值(成本成本)的生成的生成树。树。通讯网络 交通网络n最小生成树算
29、法最小生成树算法:贪婪算法(贪婪算法(greedy algorithms)u度量标准:选择使得迄今为止所记入度量标准:选择使得迄今为止所记入的那些边的成本的和数有的那些边的成本的和数有最小增量最小增量的的那条边。那条边。(1)Prims Algorithm(2)Kruskals Algorithmconst int MAXINT = 机器可表示的机器可表示的, , 问题中不可能出现的大数问题中不可能出现的大数class MinSpanTree;class MSTEdgeNode /生成树边结点类定义生成树边结点类定义friend class MinSpanTree;private: int t
30、ail, head;/生成树各边的两顶点生成树各边的两顶点 int cost; /生成树各边的代价生成树各边的代价;class MinSpanTree /生成树的类定义生成树的类定义public: MinSpanTree ( int sz = MaxEdges -1 ) : MaxSize (sz), n (0) edgevalue = new MSTEdgeNodeMaxSize; int Insert ( MSTEdgeNode &item ); /将将item加到最小生成树中加到最小生成树中protected: MSTEdgeNode *edgevalue;/边值数组边值数组 int M
31、axSize, n; /最大边数最大边数,当前边数当前边数;继续重复得:继续重复得:顶点顶点5加入生成树顶点集合:加入生成树顶点集合:v = 5v = 4(0, 5, 10), (5, 4, 25), (4, 3, 22),(3, 2, 12), (2, 1, 16), (1, 6, 14)void Graph:Prim ( MinSpanTree &T ) int NumVertices = VerticesList.last; /图中顶点数图中顶点数 lowcost = new intNumVertices; nearvex = new intNumVertices; for ( int
32、i = 1; i NumVertices; i+ ) lowcosti = Edge0i; /顶点顶点0到各边的代价到各边的代价 nearvexi = 0; /及最短带权路径及最短带权路径 nearvex0 = -1; /顶点顶点0加到生成树顶点集合加到生成树顶点集合 MSTEdgeNode e; /最小生成树结点辅助单元最小生成树结点辅助单元 for ( i = 1; i NumVertices; i+ ) /循环循环n-1次次, 加入加入n-1条边条边 int min = MAXINT; int v = 0; for ( int j = 0; j NumVertices; j+ ) if
33、( nearvexj != -1 & lowcostj min ) v = j; min = lowcostj; /求生成树外顶点到生成树内顶点具有最小求生成树外顶点到生成树内顶点具有最小 /权值的边权值的边, v是是当前具最小权值的边的位置当前具最小权值的边的位置 if ( v ) /v=0表示再也找不到要求的顶点了表示再也找不到要求的顶点了 e.tail = nearvexv; e.head = v; e.cost = lowcostv; T.Insert (e); /选出的边加入生成树选出的边加入生成树 nearvexv = -1; /作该边已加入生成树标记作该边已加入生成树标记 for
34、 ( j = 1; j NumVertices; j+ ) if ( nearvexj != -1 & /j不在生成树中不在生成树中 Edgevj lowcostj ) /需要修改需要修改 lowcostj = Edgevj; nearvexj = v; tail head cost 边的两个顶点位置边的两个顶点位置 边的权值边的权值void Graph:Kruskal ( MinSpanTree &T ) MSTEdgeNode e; /边结点辅助单元边结点辅助单元 MinHeap H(CurrentEdges); int NumVertices = VerticesList.numVex;
35、/顶点个数顶点个数 UFSets F (NumVertices); /并查集并查集F 并初始化并初始化 for ( int u = 0; u NumVertices; u+ ) /邻接矩阵邻接矩阵 for ( int v = u+1; v NumVertices; v+ ) if ( Edgeuv != MAXINT ) /检出所有边检出所有边 e.tail = u; e.head = v; e.cost = w; H.Insert (e); /插入最小堆插入最小堆 int count = 1; /最小生成树加入边数的计数最小生成树加入边数的计数 while ( count NumVertic
36、es ) / e = H.RemoveMin ( ); /从堆中退出一条边从堆中退出一条边 u = F.Find ( e.tail ); /检测两端点的根检测两端点的根v = F.Find ( e.head );if ( u != v ) /根不同根不同, 不在同一连通分量上不在同一连通分量上 F.Union ( u, v ); /合并合并 T.Insert ( e ); /该边存入最小生成树该边存入最小生成树 T 中中 count+; 源 点 终 点 最 短 路 径路 径 长 度 v0 v1(v0, v1)10 v2 (v0, v1, v2) (v0, v3, v2) 60 50 v3(v0
37、, v3)30 v4(v0, v4)(v0, v3, v4) (v0, v3, v2, v4)100 90 60const int NumVertices = 6; /图中最大顶点个数图中最大顶点个数class Graph /图的类定义图的类定义 private: int EdgeNumVerticesNumVertices; /邻接矩阵邻接矩阵 int distNumVertices; /最短路径长度数组最短路径长度数组 int pathNumVertices; /最短路径数组最短路径数组 int SNumVertices; /最短路径顶点集最短路径顶点集public: void Short
38、estPath ( const int, const int ); int choose ( const int ); void Graph:ShortestPath ( const int n, const int v )/Graph是一个具有是一个具有n个顶点的带权有向图个顶点的带权有向图, 各边上各边上/的权值由的权值由Edgeij给出。本算法建立起一个数给出。本算法建立起一个数/组组: distj, 0 j n, 是当前求到的从顶点是当前求到的从顶点v到顶点到顶点/j的最短路径长度的最短路径长度, 同时用数组同时用数组pathj, 0 j n, 存存/放求到的最短路径。放求到的最短路径
39、。 for ( int i = 0; i n; i+) disti = Edgevi; /dist数组初始化数组初始化 Si = 0; if ( i != v & disti MAXINT ) pathi = v; else pathi = -1; /path数组初始化数组初始化 Sv = 1; distv = 0;/顶点顶点v加入顶点集合加入顶点集合 /选择当前不在集合选择当前不在集合S中具有最短路径的顶点中具有最短路径的顶点u for ( i = 0; i n-1; i+ ) int min = MAXINT; int u = v; for ( int j = 0; j n; j+ ) i
40、f ( !Sj & distj min ) u = j; min = distj; Su = 1; /将顶点将顶点u加入集合加入集合S for ( int w = 0; w n; w+ ) /修改修改 if ( !Sw & Edgeuw MAXINT & distu + Edgeuw distw ) distw = distu + Edgeuw; pathw = u; 选 取 顶点1 顶点2 顶点3 顶点4 终 点 S1 d1 p1 S2 d2 p2 S3 d3 p3 S4 d4 p4 0 0 10 0 0 0 0 30 0 0 100 0 1 1 10 0 0 60 1 0 30 0 0 1
41、00 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到终到终点点v的最短路径?举顶点的最短路径?举顶点4为例为例 path4 = 2 path2 = 3 path3 = 0,反过来排列,反过来排列,得到路径得到路径0, 3, 2, 4,这就是源,这就是源点点0到终点到终点4的最短路径。的最短路径。Dijkstras AlgorithmnExample:nStep 0Dijkstras AlgorithmnExample:
42、nStep 1Dijkstras AlgorithmnExample:nStep 2Dijkstras AlgorithmnExample:nStep 3Dijkstras AlgorithmnExample:nStep 4Dijkstras AlgorithmnExample:nStep 5Dijkstras AlgorithmnExample:nStep 6void Graph:AllLengths ( const int n ) for ( int i = 0; i n; i+ ) /矩阵矩阵a与与path初始化初始化 for ( int j = 0; j n; j+ ) aij = E
43、dgeij; if ( i j & aij MAXINT ) pathij = i; / i 到到 j 有路径有路径 else pathij = 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 ( aik + akj aij ) aij = aik + akj; pathij = pathkj; /缩短路径长度缩短路径长度, 绕过绕过 k 到到 j A(-1) A(0) A(1) A(2) A(3) 0 1 2 3
44、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
45、 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学生课程学习工程图学生课程学习工程图 Indegreeclass Graph /friend class Vertex;friend class Edge;private: Vertex *NodeTable; int * n; Stack T; public: Graph ( const int vertices = 0 ) : n
46、 ( vertices ) NodeTable = new vertexn; Indegree = new intn; ; Bool TopologicalOrder ( ); Bool CriticalPath( ); Bool Graph:TopologicalSort ( ) Stack S; for ( int i = 0; i n; i+ ) /入度为零顶点进栈入度为零顶点进栈 if ( Indegreei = 0 ) S.push(i); count=0; while(S.length!=0) S.pop(j); /退栈退栈 cout j endl; /输出输出 count+; E
47、dge * l = NodeTablej.adj; while ( l ) /扫描该顶点的出边表扫描该顶点的出边表 int k = ldest; /另一顶点另一顶点 if ( -Indegreek = 0 ) /该顶点入度减一该顶点入度减一 S.push(k); /减至零减至零 l = llink; if(countG.vexnum) return Error; else return OK; , ) ,( ijjVVdurjVemaxiVe, ) ,( jijVVdurjVlminiVl事件事件 Vei Vli V0 0 0 V1 6 6 V2 4 6 V3 5 8 V4 7 7 V5 7
48、10 V6 16 16 V7 14 14 V8 18 18 边边 活动活动 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 e 0 0 0 6 4 5 7 7 7 16 14 l 0 2 3 6 6 8 7 7 10 16 14l - e 0 2 3 0 2 3 0 0 3 0 0关键关键 是是 是是 是是 是是 是是 是是/在此算法中需要对邻接表中单链表的结点加以在此算法中需要对邻接表中单链表的结点加以/修改修改, 在各结点中增加一个在各结点中增加一个int域域 cost, 记录该结记录该结/点所表示的边上的权值点所表示的边上的权值。Bool Graph:Topolog
49、icalSort ( ) Stack S; / Stack T; for ( int i = 0; i n; i+ ) /入度为零顶点进栈入度为零顶点进栈 if ( Indegreei = 0 ) S.push(i); count=0; while(S.pop(j) /退栈退栈 T.push(j); cout j endl; /输出输出 count+; Edge * l = NodeTablej.adj; while ( l ) /扫描该顶点的出边表扫描该顶点的出边表 int k = ldest; /另一顶点另一顶点 if ( -Indegreek = 0 ) /该顶点入度减一该顶点入度减一
50、S.push(k); /减至零减至零 if(vej+l-costvek) vek=vej+l-cost; l = llink; if(countG.vexnum) return Error; return OK;Bool graph:CriticalPath ( ) if(!TopologicalSort ( ) return Error; for ( i = 0; i n; i+ ) Vli = Ven-1;While(T.pop(j) Edge * l = NodeTablej.adj; while ( l ) /扫描该顶点的出边表扫描该顶点的出边表 int k = ldest; /另一顶