什么是图
表示“多对多”的关系 包含
一组顶点:通常用V(Vertex)表示顶点集合 一组边:通常用E(Edge)表示边的集合
抽象数据类型
类型名称:图(Gragh) 数据对象集:G(V,E)由一个非空的有限顶点集合V和一个优先边集合E组成 操作集:对于任意图G,以及v,e
Gragh Create ( ) : 建立空图并返回空图
Graph InsertVertex ( Graph G, Vertex v) :将v插入G
Graph InsertEdge ( Graph G, Edge e) : 将e插入G
void DFS ( Graph G, Vertex v) : 从顶点v出发深度优先遍历图G
void BFS ( Graph G, Vertex v) : 从顶点v出发宽度优先遍历图G
void ShortestPath ( Graph G, Vertex v, int Dist[ ] ) : 计算图G中顶点v到任意其他顶点的最短距离
void MST ( Graph G) : 计算图G的最小生成树
常见术语
怎么在程序中表示一个图
1. 邻接矩阵
对角元为0 对称矩阵 问题:对于无向图的存储,怎么可以省一半的空间?
用一个长度为N(N)/2
的一维数组A存储{
G
00
,
G
10
,
G
11
,
.
.
.
,
G
n
−
1
n
−
1
G_{00},G_{10},G_{11},...,G_{n-1 n-1}
G 0 0 , G 1 0 , G 1 1 , . . . , G n − 1 n − 1 },则
G
i
j
G_{ij}
G i j 在A中的对应的下标是:( i*(i+1)/2 + j ) 对于网路,只要把G[i] [j]的值定义为边
<
v
i
,
v
j
>
<v_i,v_j>
< v i , v j > 的权重即可。 有什么好处?
直观,简单,好理解 方便检查任意一对顶点间是否存在边 方便找任一顶点的所有“邻接点” 方便计算任一顶点的“度”(“出度”、“入度”) 有什么不好?
浪费空间(稀疏图有大量无效元素)
浪费时间(统计边数)
2. 邻接表
G[N]为指针数组,对应矩阵每行一个链表,只存非零元素 一定要够稀疏才合算 好处
方便找任一顶点的所有“邻接点” 节约稀疏图的空间
需要N个头指针 + 2E个结点(每个结点至少2个域) 方便计算任一顶点的“度”?
对无向图:是的 对有向图:只能计算“出度”;需要构造“逆邻接表”(存指向自己的边)来方便计算“入度” 不方便检查任意一对顶点是否存在边
#define MaxVertexNum 100
#define INFINITY 65535
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
typedef struct ENode * PtrToENode;
struct ENode{
Vertex V1, V2;
WeightType Weight;
} ;
typedef PtrToENode Edge;
typedef struct GNode * PtrToGNode;
struct GNode{
int Nv;
int Ne;
WeightType G[ MaxVertexNum] [ MaxVertexNum] ;
DataType Data[ MaxVertexNum] ;
} ;
typedef PtrToGNode MGraph;
MGraph CreateGraph ( int VertexNum )
{
Vertex V, W;
MGraph Graph;
Graph = ( MGraph) malloc ( sizeof ( struct GNode) ) ;
Graph-> Nv = VertexNum;
Graph-> Ne = 0 ;
for ( V= 0 ; V< Graph-> Nv; V++ )
for ( W= 0 ; W< Graph-> Nv; W++ )
Graph-> G[ V] [ W] = INFINITY;
return Graph;
}
void InsertEdge ( MGraph Graph, Edge E )
{
Graph-> G[ E-> V1] [ E-> V2] = E-> Weight;
Graph-> G[ E-> V2] [ E-> V1] = E-> Weight;
}
MGraph BuildGraph ( )
{
MGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf ( "%d" , & Nv) ;
Graph = CreateGraph ( Nv) ;
scanf ( "%d" , & ( Graph-> Ne) ) ;
if ( Graph-> Ne != 0 ) {
E = ( Edge) malloc ( sizeof ( struct ENode) ) ;
for ( i= 0 ; i< Graph-> Ne; i++ ) {
scanf ( "%d %d %d" , & E-> V1, & E-> V2, & E-> Weight) ;
InsertEdge ( Graph, E ) ;
}
}
for ( V= 0 ; V< Graph-> Nv; V++ )
scanf ( " %c" , & ( Graph-> Data[ V] ) ) ;
return Graph;
}
#define MaxVertexNum 100
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
typedef struct ENode * PtrToENode;
struct ENode{
Vertex V1, V2;
WeightType Weight;
} ;
typedef PtrToENode Edge;
typedef struct AdjVNode * PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV;
WeightType Weight;
PtrToAdjVNode Next;
} ;
typedef struct Vnode{
PtrToAdjVNode FirstEdge;
DataType Data;
} AdjList[ MaxVertexNum] ;
typedef struct GNode * PtrToGNode;
struct GNode{
int Nv;
int Ne;
AdjList G;
} ;
typedef PtrToGNode LGraph;
LGraph CreateGraph ( int VertexNum )
{
Vertex V;
LGraph Graph;
Graph = ( LGraph) malloc ( sizeof ( struct GNode) ) ;
Graph-> Nv = VertexNum;
Graph-> Ne = 0 ;
for ( V= 0 ; V< Graph-> Nv; V++ )
Graph-> G[ V] . FirstEdge = NULL ;
return Graph;
}
void InsertEdge ( LGraph Graph, Edge E )
{
PtrToAdjVNode NewNode;
NewNode = ( PtrToAdjVNode) malloc ( sizeof ( struct AdjVNode) ) ;
NewNode-> AdjV = E-> V2;
NewNode-> Weight = E-> Weight;
NewNode-> Next = Graph-> G[ E-> V1] . FirstEdge;
Graph-> G[ E-> V1] . FirstEdge = NewNode;
NewNode = ( PtrToAdjVNode) malloc ( sizeof ( struct AdjVNode) ) ;
NewNode-> AdjV = E-> V1;
NewNode-> Weight = E-> Weight;
NewNode-> Next = Graph-> G[ E-> V2] . FirstEdge;
Graph-> G[ E-> V2] . FirstEdge = NewNode;
}
LGraph BuildGraph ( )
{
LGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf ( "%d" , & Nv) ;
Graph = CreateGraph ( Nv) ;
scanf ( "%d" , & ( Graph-> Ne) ) ;
if ( Graph-> Ne != 0 ) {
E = ( Edge) malloc ( sizeof ( struct ENode) ) ;
for ( i= 0 ; i< Graph-> Ne; i++ ) {
scanf ( "%d %d %d" , & E-> V1, & E-> V2, & E-> Weight) ;
InsertEdge ( Graph, E ) ;
}
}
for ( V= 0 ; V< Graph-> Nv; V++ )
scanf ( " %c" , & ( Graph-> G[ V] . Data) ) ;
return Graph;
}