1、第8章 图8-1 画出1个顶点、2个顶点、3个顶点、4个顶点与5个顶点得无向完全图。试证明在n个顶点得无向完全图中,边得条数为n(n-1)/2。【解答】2个顶点得无向完全图1个顶点得无向完全图5个顶点得无向完全图4个顶点得无向完全图3个顶点得无向完全图【证明】在有n个顶点得无向完全图中,每一个顶点都有一条边与其它某一顶点相连,所以每一个顶点有n-1条边与其她n-1个顶点相连,总计n个顶点有n(n-1)条边。但在无向图中,顶点i到顶点j与顶点j到顶点i就是同一条边,所以总共有n(n-1)/2条边。DA8-2 右边得有向图就是强连通得吗?请列出所有得简单路径。EB【解答】FC判断一个有向图就是否强
2、连通,要瞧从任一顶点出发就是否能够回到该顶点。右面得有向图做不到这一点,它不就是强连通得有向图。各个顶点自成强连通分量。所谓简单路径就是指该路径上没有重复得顶点。从顶点A出发,到其她得各个顶点得简单路径有AB,ADB,ABC,ADBC,AD,ABE,ADE,ADBE,ABCFE,ADBCFE,ABCF,ADBCF。从顶点B出发,到其她各个顶点得简单路径有BC,BCF,BE,BCFE。从顶点C出发,到其她各个顶点得简单路径有CF,CFE。从顶点D出发,到其她各个顶点得简单路径有DB,DBC,DBCF,DE,DBE,DBCFE。从顶点E出发,到其她各个顶点得简单路径无。从顶点F出发,到其她各个顶点
3、得简单路径有FE。8-3 给出右图得邻接矩阵、邻接表与邻接多重表表示。DA【解答】(1) 邻接矩阵EBFC310 A1 B2 C3 D4 E5 F(2) 邻接表42(出边表)54140 A1 B2 C3 D4 E5 F(入边表)30105312data fin fout(3) 邻接多重表(十字链表)i j ilink jlink0 1 (A, B)0 A1 B2 C3 D4 E5 F0 3 (A, D)1 2 (B, C)1 4 (B, E)2 5 (C, F)3 1 (D, B)3 4 (D, E)5 4 (F, E)8-4 用邻接矩阵表示图时,若图中有1000个顶点,1000条边,则形成得
4、邻接矩阵有多少矩阵元素?有多少非零元素?就是否稀疏矩阵?【解答】一个图中有1000个顶点,其邻接矩阵中得矩阵元素有10002 = 1000000个。它有1000个非零元素(对于有向图)或2000个非零元素(对于无向图),因此就是稀疏矩阵。8-5 用邻接矩阵表示图时,矩阵元素得个数与顶点个数就是否相关?与边得条数就是否相关?【解答】用邻接矩阵表示图,矩阵元素得个数就是顶点个数得平方,与边得条数无关。矩阵中非零元素得个数与边得条数有关。8-6 有n个顶点得无向连通图至少有多少条边?有n个顶点得有向强连通图至少有多少条边?试举例说明。【解答】n个顶点得无向连通图至少有n-1条边,n个顶点得有向强连通
5、图至少有n条边。例如:特例情况就是当n = 1时,此时至少有0条边。8-7对于有n个顶点得无向图,采用邻接矩阵表示,如何判断以下问题: 图中有多少条边?任意两个顶点i与j之间就是否有边相连?任意一个顶点得度就是多少?【解答】用邻接矩阵表示无向图时,因为就是对称矩阵,对矩阵得上三角部分或下三角部分检测一遍,统计其中得非零元素个数,就就是图中得边数。如果邻接矩阵中Aij 不为零,说明顶点i与顶点j之间有边相连。此外统计矩阵第i行或第i列得非零元素个数,就可得到顶点i得度数。8-8对于如右图所示得有向图,试写出:(1) 从顶点出发进行深度优先搜索所得到得深度优先生成树;(2) 从顶点出发进行广度优先
6、搜索所得到得广度优先生成树; 【解答】(1) 以顶点为根得深度优先生成树(不唯一): 或 (2) 以顶点为根得广度优先生成树:8-9 试扩充深度优先搜索算法,在遍历图得过程中建立生成森林得左子女-右兄弟链表。算法得首部为void Graph:DFS ( const int v, int visited , TreeNode * t ) 其中,指针t指向生成森林上具有图顶点v信息得根结点。(提示:在继续按深度方向从根v得某一未访问过得邻接顶点w向下遍历之前,建立子女结点。但需要判断就是作为根得第一个子女还就是作为其子女得右兄弟链入生成树。)【解答】为建立生成森林,需要先给出建立生成树得算法,然后
7、再在遍历图得过程中,通过一次次地调用这个算法,以建立生成森林。te mplate void Graph : DFS_Tree ( const int v, int visited , TreeNode *t ) /从图得顶点v出发, 深度优先遍历图, 建立以t (已在上层算法中建立)为根得生成树。 Visitedv = 1; int first = 1; TreeNode * p, * q; int w = GetFirstNeighbor ( v ); /取第一个邻接顶点 while ( w != -1 ) /若邻接顶点存在 if ( vositedw = 0 ) /且该邻接结点未访问过 p
8、 = new TreeNode ( GetValue (w) );/建立新得生成树结点 if ( first = 1 )/若根*t还未链入任一子女 t-setFirstChild ( p ); first = 0; /新结点*p成为根*t得第一个子女 else q-setNextSibling ( p );/否则新结点*p成为*q得下一个兄弟 q = p;/指针q总指示兄弟链最后一个结点 DFS_Tree ( w, visited, q );/从*q向下建立子树 w = GetNextNeighbor ( v, w );/取顶点v排在邻接顶点w得下一个邻接顶点 下一个算法用于建立以左子女-右兄
9、弟链表为存储表示得生成森林。template void Graph : DFS_Forest ( Tree & T ) /从图得顶点v出发, 深度优先遍历图, 建立以左子女-右兄弟链表表示得生成森林T。 T、root = NULL; int n = NumberOfVertices ( );/顶点个数 TreeNode * p, * q; int * visited = new int n ;/建立访问标记数组 for ( int v = 0; v n; v+ ) visitedv = 0; for ( v = 0; v n; v+ ) /逐个顶点检测 if ( visitedv = 0 )
10、/若尚未访问过 p = new TreeNode ( GetValue ( v ) );/建立新结点*p if ( T、root = NULL ) T、root = p;/原来就是空得生成森林, 新结点成为根 else q- setNextSibling ( p );/否则新结点*p成为*q得下一个兄弟 q = p; DFS_Tree ( v, visited, p );/建立以*p为根得生成树 8-10 用邻接表表示图时,顶点个数设为n,边得条数设为e,在邻接表上执行有关图得遍历操作时,时间代价就是O(n*e)?还就是O(n+e)?或者就是O(max(n,e)?【解答】在邻接表上执行图得遍历
11、操作时,需要对邻接表中所有得边链表中得结点访问一次,还需要对所有得顶点访问一次,所以时间代价就是O(n+e)。8-11 右图就是一个连通图,请画出 (1) 以顶点为根得深度优先生成树;(2) 如果有关节点,请找出所有得关节点。(3) 如果想把该连通图变成重连通图,至少在图中加几条边?如何加?【解答】(1) 以顶点为根得深度优先生成树:(2) 关节点为 ,(3) 至少加四条边 (1, 10), (3, 4), (4, 5), (5, 6)。从得子孙结点到得祖先结点引一条边,从得子孙结点到根得另一分支引一条边,并将得子孙结点、与结点连结起来,可使其变为重连通图。8-12试证明在一个有n个顶点得完全
12、图中,生成树得数目至少有2n-1-1。【证明】略71110758698-13 编写一个完整得程序,首先定义堆与并查集得结构类型与相关操作,再定义Kruskal求连通网络得最小生成树算法得实现。并以右图为例,写出求解过程中堆、并查集与最小生成树得变化。【解答】求解过程得第一步就是对所有得边,按其权值大小建堆:3 4 51 2 71 2 71 2 72 3 102 3 102 4 92 3 101 3 111 3 11加(1, 2), (1, 3), (2,3)2 4 91 3 11加(3, 4)加(2, 4)3 4 53 4 53 5 71 2 71 2 73 5 73 6 82 3 102 4
13、 91 3 111 3 112 3 102 4 9加(3, 5)加(3, 6)3 4 53 5 75 6 6加(5, 6)3 6 82 3 102 4 91 2 71 3 11求解过程中并查集与堆得变化:1 2 75 6 63 6 83 5 73 5 71 2 72 3 102 4 91 3 11选(5,6,6)3 6 82 3 102 4 91 3 11选(3,4,5)3 6 83 5 72 3 103 6 82 4 92 3 101 3 11选(3,5,7)2 4 91 3 11选(1,2,7)2 3 102 4 91 3 112 3 101 3 11选(2,4,9), 结束选(3,6,8
14、), 在同一连通分量上, 不加最后得到得生成树如下0 1 2 3 4 5 63 1 -6 3 3 5 7567并查集得存储表示9完整得程序如下:#include template class MinHeap public: enum MaxHeapSize = 50 ; MinHeap ( int Maxsize = MaxHeapSize ); MinHeap ( Type Array , int n ); void Insert ( const Type &ele ); void RemoveMin ( Type &Min ); void Output (); private: void
15、FilterDown ( int start, int end ); void FilterUp ( int end ); Type*pHeap; int HMaxSize; int CurrentSize;class UFSets public: enum MaxUnionSize = 50 ; UFSets ( int MaxSize = MaxUnionSize ); UFSets () delete m_pParent; void Union ( int Root1, int Root2 ); int Find ( int x );private: int m_iSize; int *
16、m_pParent;class Graph public: enum MaxVerticesNum = 50 ; Graph( int Vertices = 0) CurrentVertices = Vertices; InitGraph(); void InitGraph (); void Kruskal (); int GetVerticesNum () return CurrentVertices; private: int EdgeMaxVerticesNumMaxVerticesNum; int CurrentVertices;class GraphEdge public: int
17、head, tail; int cost; int operator = ( GraphEdge &ed );GraphEdge : operator cost = ed、cost;UFSets : UFSets ( int MaxSize ) m_iSize = MaxSize; m_pParent = new intm_iSize; for ( int i = 0; i = 0 ) x = m_pParentx; return x;template MinHeap : MinHeap ( int Maxsize ) HMaxSize = Maxsize; pHeap = new TypeH
18、MaxSize; CurrentSize = -1;template MinHeap : MinHeap ( Type Array, int n ) HMaxSize = ( n MaxHeapSize ) ? MaxHeapSize : n; pHeap = new TypeHMaxSize; for ( int i = 0; i = 0 ) FilterDown ( iPos, CurrentSize ); iPos-; template void MinHeap : FilterDown ( int start, int end ) int i = start, j = 2 * star
19、t + 1; Type Temp = pHeapi; while ( j = end ) if ( j end & pHeapj+1 = pHeapj ) j+; if ( Temp = pHeapj ) break; pHeapi = pHeapj; i = j; j = 2 * j + 1; pHeapi = Temp;template void MinHeap : FilterUp ( int end ) int i = end, j = ( end - 1 ) / 2; Type Temp = pHeapi; while ( i 0 ) if ( pHeapj = Temp ) bre
20、ak; pHeapi = pHeapj; i = j; j = ( j - 1 ) / 2; pHeapi = Temp;template void MinHeap : Insert ( const Type &ele ) CurrentSize+; if ( CurrentSize = HMaxSize ) return; pHeapCurrentSize = ele; FilterUp ( CurrentSize );template void MinHeap : RemoveMin ( Type &Min ) if ( CurrentSize 0 ) return; Min = pHea
21、p0; pHeap0 = pHeapCurrentSize-; FilterDown ( 0, CurrentSize );template void MinHeap : Output ( ) for ( int i = 0; i = CurrentSize; i+ ) cout pHeapi ; cout endl;void Graph : InitGraph( ) Edge00 = -1; Edge01 = 28; Edge02 = -1; Edge03 = -1; Edge04 = -1; Edge05 = 10; Edge06 = -1; Edge11 = -1; Edge12 = 1
22、6; Edge13 = -1; Edge14 = -1; Edge15 = -1; Edge16 = 14; Edge22 = -1; Edge23 = 12; Edge24 = -1; Edge25 = -1; Edge26 = -1; Edge33 = -1; Edge34 = 22; Edge35 = -1; Edge36 = 18; Edge44 = -1; Edge45 = 25; Edge46 = 24; Edge55 = -1; Edge56 = -1; Edge66 = -1; for ( int i = 1; i 6; i+ ) for ( int j = 0; j i; j
23、 + ) Edgeij = Edgeji;void Graph : Kruskal( ) GraphEdge e; int VerticesNum = GetVerticesNum ( ); int i, j, count; MinHeap heap ( VerticesNum *VerticesNum ); UFSets set ( VerticesNum ); for ( i = 0; i VerticesNum; i+ ) for ( j = i + 1; j 0 ) e、head = i; e、tail = j; e、cost = Edgeij;heap、Insert ( e ); c
24、ount = 1; while ( count VerticesNum ) heap、RemoveMin ( e ); i = set、Find ( e、head ); j = set、Find ( e、tail ); if ( i != j ) set、Union ( i, j );count+;cout ( e、head , e、tail , e、cost ) endl; 8-14 利用Dijkstra算法得思想,设计一个求最小生成树得算法。【解答】计算连通网络得最小生成树得Dijkstra算法可描述如下:将连通网络中所有得边以方便得次序逐步加入到初始为空得生成树得边集合T中。每次选择并加
25、入一条边时,需要判断它就是否会与先前加入T得边构成回路。如果构成了回路,则从这个回路中将权值最大得边退选。 下面以邻接矩阵作为连通网络得存储表示,并以并查集作为判断就是否出现回路得工具,分析算法得执行过程。165192192614611181616215211419149并查集, 表明4个结点在同一连通分量上161651951914914116616最终得到得最小生成树为561411实现算法得过程为const int MaxNum = 10000;void Graph : Dijkstra ( ) GraphEdge e; int VerticesNum = GetVerticesNum (
26、); int i, j, p, q, k; int disJointVerticesNum;/并查集 for ( i = 0; i VerticesNum; i+ ) disJointi = -1; /并查集初始化 for ( i = 0; i VerticesNum-1; i+ )/检查所有得边 for ( j = i + 1; j VerticesNum; j+ ) if ( Edgeij = 0 ) p = disJointp; while ( disJointq = 0 ) p = disJointq; if ( p != q ) disJointj = i;/ i与j不在同一连通分量
27、上, 连通之 else / i与j在同一连通分量上 p = i;/寻找离结点i与j最近得祖先结点 while ( disJointp = 0 ) /每变动一个p, 就对q到根得路径检测一遍 q = j; while ( disJointq = 0 & disJointq = disJointp )q = disJointq; if ( disJointq = disJointp ) break; else p = disJointp; k = disJointp;/结点k就是i与j得最近得共同祖先 p = i; q = disJointp; max = -MaxNum;/从i到k找权值最大得边
28、(s1, s2) while ( q max ) max = Edgeqp; s1 = p; s2 = q; p =q; q = disJointp; p = j; q = disJointp; max = -MaxNum;/从j到k找权值最大得边(t1, t2) while ( q max ) max = Edgeqp; t1 = p; t2 = q; p =q; q = disJointp; max = Edgeij; k1 = i; k2 = j; if ( max Edges1s2 ) max = Edges1s2; k1 = s1; k2 = s2; if ( max Edget1t
29、2 ) max = Edget1t2; k1 = t1; k2 = t2; if ( max != Edgeij ) /当Edgeij = max时边不改 if ( disJointk1 = k2 ) disJointk1 = -1; else disJointk2 = -1;/删除权值最大得边 disJointj = i;/加入新得边 Edgeji = - Edgeji; 8-15以右图为例,按Dijkstra算法计算得到得从顶点(A)到其它各个顶点得最短路径与最短路径长度。A1810【解答】源点终点最短路径最短路径长度CB5 A B(A,B)(A,B)(A,B)(A,B)101010102
30、25 C(A,C)(A,C)(A,C)(A,C)18181818 D (A,B,D)(A,B,D)(A,B,D)151515ED2 E (A,B,D,E)(A,B,D,E)17178-16 在以下假设下,重写Dijkstra算法:(1) 用邻接表表示带权有向图G,其中每个边结点有3个域:邻接顶点vertex,边上得权值length与边链表得链接指针link。(2) 用集合T = V(G) - S代替S (已找到最短路径得顶点集合),利用链表来表示集合T。试比较新算法与原来得算法,计算时间就是快了还就是慢了,给出定量得比较。【解答】(1) 用邻接表表示得带权有向图得类定义:const int D
31、efaultSize = 10;/缺省顶点个数class Graph;/图得前视类定义struct Edge /边得定义friend class Graph; int vertex;/边得另一顶点位置 float length;/边上得权值 Edge *link;/下一条边链指针 Edge ( ) /构造函数 Edge ( int num, float wh ) : vertex (num), length (wh), link (NULL) /构造函数 int operator = 0 & i NumVertices ? NodeTablei、data : ; float GetWeight
32、 ( int v1, int v2 );/返回边(v1, v2)上得权值 int GetFirstNeighbor ( int v );/取顶点v得第一个邻接顶点 int GetNextNeighbor ( int v, int w );/取顶点v得邻接顶点w得下一个邻接顶点(2) 用集合T = V(G) - S代替S (已找到最短路径得顶点集合),利用链表来表示集合T。 8-17 试证明:对于一个无向图G = (V, E),若G中各顶点得度均大于或等于2,则G中必有回路。【解答】反证法:对于一个无向图G=(V,E),若G中各顶点得度均大于或等于2,则G中没有回路。此时从某一个顶点出发,应能按
33、拓扑有序得顺序遍历图中所有顶点。但当遍历到该顶点得另一邻接顶点时,又可能回到该顶点,没有回路得假设不成立。8-18 设有一个有向图存储在邻接表中。试设计一个算法,按深度优先搜索策略对其进行拓扑排序。并以右图为例检验您得算法得正确性。【解答】(1) 利用题8-16定义得邻接表结构。增加两个辅助数组与一个工作变量:w 记录各顶点入度 int indegreeNumVertices。w 记录各顶点访问顺序 int visitedNumVertices,初始时让visitedi = 0, i = 1, 2, , NumVertices。w 访问计数int count,初始时为0。(2) 拓扑排序算法void Graph : dfs ( int visited , int indegree , int v, int & count )