目录
图的定义:
:在无向图中,顶点v的是指与该顶点相关联的边数,通常记为TD (v)。
无向完全图:
有向完全图:
含有n个顶点的无向完全图有多少条边? n×(n-1)/2条边 含有n个顶点的有向完全图有多少条弧? n×(n-1)条边
稀疏图:称边数很少的图为稀疏图;通常边数<<n^2
稠密图:称边数很多的图为稠密图。
权:是指对边赋予的有意义的数值量。
网:边上带权的图,也称网图。
路径:
路径长度:
回路(环):
简单路径:
简单回路(简单环):
子图:
连通图:
连通分量:
强连通图:有向图
强连通分量:的极大强连通子图。
生成树:连通图全部顶点
生成森林:非连通图生成森林
图的抽象数据类型:
ADT Graph {
数据对象V:v是具有相同特性的数据元素的集合,称为顶点集。
数据关系R:R={VR};VR={<v,w>|v,w∈V 且 P(v,w),
<v,w>表示从v到w的弧,
谓词P(v,w)定义了弧<v,w>的意义或信息}
基本操作P:
CreatGraph ( &G, V,VR);
初始条件:V是图的顶点集,VR是图中弧的集合。
操作结果:按V和VR的定义构造图G。
InsertVex ( &G, v);
初始条件:图G存在,v和图中顶点有相同特征。
操作结果:在图G中添加新顶点。
………………(参见P156-257)
}
是否可以采用顺序存储结构存储图?
代码
Typedef struct ArcCell { //弧(边)结点的定义
VRType adj; // VRType可以是int类型
//顶点间关系,无权图取1或0;有权图取权值类型
InfoType *info;
} ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
Typedef enum {DG,DN,UDG,UDN} GraphKind
Typedef struct { //图的定义
VertexType vexs [MAX_VERTEX_NUM ] ; //顶点表
// VertexType 可以是char类型
AdjMatrix arcs; //邻接矩阵
int Vernum, arcnum; //顶点总数,弧(边)总数
GraphKind kind; //图的种类标志
} Mgraph;
图的邻接表存储表示
Typedef struct VNode{ //头结点结构-顶点结构
VertexType data; //顶点信息
ArcNode * firstarc; //指向依附该顶点的第一条弧的指针
} VNode, AdjList[ MAX_VERTEX_NUM ];
Typedef struct ArcNode { //表结点结构-边结构
int adjvex; //该弧所指向的顶点位置
struct ArcNode *nextarcs; //指向下一条弧的指针
InfoArc *info; //该弧相关信息的指针
} ArcNode;
Typedef struct { //图结构
AdjList vertics ; //应包含邻接表
int vexnum, arcnum; //应包含顶点总数和弧总数
int kind; //还应说明图的种类(用标志)
} ALGraph;
基本思想 :
深度优先搜索(遍历)步骤:
计算机如何实现DFS?
在图的邻接表中如何进行DFS?
深度优先遍历图的算法:
Boolean visited [MAX]; // 访问标志数组
Status (*VisitFunc) (int v); //函数指针变量
Void DFSTraverse( Graph G, Status (*Visit) (int v))
{ // 对图G做深度优先遍历
VisitFunc = Visit;
// 使用全局变量VisitFunc,使DFS不必设函数指针参数
for (v=0; v<G.vexnum; ++v) visited[v] = FALSE;
// 访问标志数组初始化
for (v=0; v<G.vexnum; ++v) //对于连通图从某一个结点出发就可以访问到所有结点,如果是非连通图需要从多个结点出发才能访问所有结点。
if (!visited[v]) DFS(G,v); // 对尚未访问的顶点调用DFS
}
Void DFS (Graph G, int v)
{ // 从第v个顶点出发递归地深度优先遍历图G
visited [v] = TRUE; VisitFunc (v);
//访问第v个顶点
for ( w = FirstAdjVex (G, v); w>=0;
w = NextAdjVex (G, v, w) )
if (!visited[w]) DFS(G,w);
// 对v的尚未访问的邻接点w递归调用DFS
}
Status FirstAdjVex(MGraph G,int v)
{ int i;
for(i=0;i<G.vexnum;i++)
if(G.arcs[v][i].adj!=INFINITY)
return i;
return OVERFLOW;}
Status NextAdjVex(MGraph G,int v,int w)
{ int i;
for(i=w+1;i<G.vexnum;i++)
if(G.arcs[v][i].adj!=INFINITY)
return i;
return OVERFLOW;}
BFS算法如何编程?
Void BFSTraverse ( Graph G, Status (* Visit) (int v) )
{ for ( v=0; v<G.vexnum; ++v ) visited[v] = FALSE ;
InitQuene(Q); // 置空的辅助队列Q
for ( v=0; v<G.vexnum; ++v )
if (!visited[v] ) // v尚未访问
{ visited[v] = TRUE; Visit(v);
EnQuene (Q,v); // v入队列
while (!QueneEmpty(Q))
{ DeQuene (Q, u); // 队头元素出队并置为u
for (w=FirstAdjVex(G,u); w>=0; w=NextAdjVex(G,u,w))
if (! Visited [w] ) // w为u的尚未访问的邻接顶点
{ Visited [w] = TRUE; Visit(w);
EnQuene (Q,w);
} //if
} //while
} // if
} // BFSTraverse
void MiniSpanTree_P ( MGraph G, VertexType u)
{ //用普里姆算法从顶点u出发构造网G的最小生成树
k = LocateVex ( G, u );
for ( j=0; j<G.vexnum; ++j ) // 辅助数组初始化
if (j!=k)
closedge[j] = { u, G.arcs[k][j].adj };
closedge[k].lowcost = 0; // 初始,U={u}
for (i=0; i<G.vexnum; ++i) //选择其余G.vexnum-1个顶点
{ k = minimum(closedge);
// 求出T的下一个结点:第k顶点
printf (closedge[k].adjvex, G.vexs[k]);
// 输出生成树上一条边和对应的顶点
closedge[k].lowcost = 0; // 第k顶点并入U集
for (j=0; j<G.vexnum; ++j) //修改其它顶点的最小边
if (G.arcs[k][j].adj < closedge[j].lowcost)
//新顶点并入U后重新选择最小边
closedge[j] = { G.vexs[k], G.arcs[k][j].adj };
}//for
}// MiniSpanTree
进行拓扑排序的方法:重复选择没有直接前驱的顶点。
Dijkstra算法描述:
(1)设A[n][n]为有向网的带权邻接矩阵,
A[i][j]表示弧(vi,vj )的权值,
S为已找到从源点v0出发的最短路径的终点集合,初始状态为{v0}.
辅助数组dist[n]为各终点当前找到的最短路径的长度,初始值为: dist[i]=A[v0 ,i]
//即邻接矩阵中第v0行的权值
(2)选择u,使得
dist[u]=min{ dist[w] | w∈V-S }
// w是S集之外的顶点,dist[u]是从源点v0到S集外所有顶点
的弧中最短的一条
S= S ∪{u} //将u加入S集
(3)对于所有不在S中的终点w,若
dist[u]+ A[u,w]< dist[w]
// 即(v0,u)+(u,w)<(v0,w)
则修改dist[w]为: dist[w]= dist[u]+ A[u,w]
(4)重复操作(2)、(3)共n-1次,由此求得从v0到各终点的最短路径。
一、单选题 ( c )1. 在一个图中,所有顶点的度数之和等于图的边数的 倍。 A.1/2 B. 1 C. 2 D. 4 ( b )2. 在一个有向图中,所有顶点的入度之和等于所有顶点的出度之和的 倍。 A.1/2 B. 1 C. 2 D. 4 ( b )3. 有8个结点的无向图最多有 条边。 A.14 B. 28 C. 56 D. 112 ( c )4. 有8个结点的无向连通图最少有 条边。 A.5 B. 6 C. 7 D. 8 ( c )5. 有8个结点的有向完全图有 条边。 A.14 B. 28 C. 56 D. 112 ( b )6. 用邻接表表示图进行广度优先遍历时,通常是采用 来实现算法的。 A.栈 B. 队列 C. 树 D. 图 ( a )7. 用邻接表表示图进行深度优先遍历时,通常是采用 来实现算法的。 A.栈 B. 队列 C. 树 D. 图 ( c )8. 已知图的邻接矩阵,根据算法思想,则从顶点0出发按深度优先遍历的结点序列是
( d )9. 已知图的邻接矩阵同上题8,根据算法,则从顶点0出发,按深度优先遍历的结点序列是 A. 0 2 4 3 1 5 6 B. 0 1 3 5 6 4 2 C. 0 4 2 3 1 6 5 D. 0 1 3 4 2 5 6 (b)10. 已知图的邻接矩阵同上题8,根据算法思想,则从顶点0出发,按广度优先遍历的结点序列是 A. 0 2 4 3 6 5 1 B. 0 1 3 6 4 2 5 C. 0 4 2 3 1 5 6 D. 0 1 3 4 2 5 6 ( c )11. 已知图的邻接矩阵同上题8,根据算法,则从顶点0出发,按广度优先遍历的结点序列是 A. 0 2 4 3 1 6 5 B. 0 1 3 5 6 4 2 C. 0 1 2 3 4 6 5 D. 0 1 2 3 4 5 6 ( d )12. 已知图的邻接表如下所示,根据算法,则从顶点0出发按深度优先遍历的结点序列是
( a )13. 已知图的邻接表如下所示,根据算法,则从顶点0出发按广度优先遍历的结点序列是
( a )14. 深度优先遍历类似于二叉树的 A.先序遍历 B. 中序遍历 C. 后序遍历 D. 层次遍历 ( d )15. 广度优先遍历类似于二叉树的 A.先序遍历 B. 中序遍历 C. 后序遍历 D. 层次遍历 ( a )16. 任何一个无向连通图的最小生成树 A.只有一棵 B. 一棵或多棵 C. 一定有多棵 D. 可能不存在 (注,生成树不唯一,但最小生成树唯一,即边权之和或树权最小的情况唯一) 二、填空题 1. 图有 邻接矩阵 、 邻接表 等存储结构,遍历图有 深度优先遍历 、 广度优先遍历 等方法。 2. 有向图G用邻接表矩阵存储,其第i行的所有元素之和等于顶点i的 出度 。 3. 如果n个顶点的图是一个环,则它有 n 棵生成树。 (以任意一顶点为起点,得到n-1条边) 4. n个顶点e条边的图,若采用邻接矩阵存储,则空间复杂度为 O(n2) 。 5. n个顶点e条边的图,若采用邻接表存储,则空间复杂度为 O(n+e) 。 6. 设有一稀疏图G,则G采用 邻接表 存储较省空间。 7. 设有一稠密图G,则G采用 邻接矩阵 存储较省空间。 8. 图的逆邻接表存储结构只适用于 有向 图。 9. 已知一个图的邻接矩阵表示,删除所有从第i个顶点出发的方法是 将邻接矩阵的第i行全部置0 。 10. 图的深度优先遍历序列 不是 惟一的。 11. n个顶点e条边的图采用邻接矩阵存储,深度优先遍历算法的时间复杂度为 O(n2) ;若采用邻接表存储时,该算法的时间复杂度为 O(n+e) 。 12. n个顶点e条边的图采用邻接矩阵存储,广度优先遍历算法的时间复杂度为 O(n2) ;若采用邻接表存储,该算法的时间复杂度为 O(n+e) 。 13. 图的BFS生成树的树高比DFS生成树的树高 小或相等 。 14. 用普里姆(Prim)算法求具有n个顶点e条边的图的最小生成树的时间复杂度为 O(n2) ;用克鲁斯卡尔(Kruskal)算法的时间复杂度是 O(elog2e) 。 15. 若要求一个稀疏图G的最小生成树,最好用 克鲁斯卡尔(Kruskal) 算法来求解。 16. 若要求一个稠密图G的最小生成树,最好用 普里姆(Prim) 算法来求解。 17. 用Dijkstra算法求某一顶点到其余各顶点间的最短路径是按路径长度 递增 的次序来得到最短路径的。 18. 拓扑排序算法是通过重复选择具有 0 个前驱顶点的过程来完成的。 三、分析求解题