一. 图的逻辑结构
1. 图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:
G=(V,E)
其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合。
在线性表中,元素个数可以为零,称为空表;
在树中,结点个数可以为零,称为空树;
在图中,顶点个数不能为零,但可以没有边。
2.图的基本术语
简单图:在图中,若不存在顶点到其自身的边,且同一条边不重复出现。
邻接、依附
无向图中,对于任意两个顶点vi和顶点vj,若存在边(vi,vj),则称顶点vi和顶点vj互为邻接点,同时称边(vi,vj)依附于顶点vi和顶点vj。
有向图中,对于任意两个顶点vi和顶点vj,若存在弧<vi,vj>,则称顶点vi邻接到顶点vj,顶点vj邻接自顶点vi,同时称弧<vi,vj>依附于顶点vi和顶点vj 。
无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。
有向完全图:在有向图中,如果任意两个顶点之间都存在方向相反的两条弧,则称该图为有向完全图。
稀疏图:称边数很少的图为稀疏图;
稠密图:称边数很多的图为稠密图。
顶点的度:在无向图中,顶点v的度是指依附于该顶点的边数,通常记为TD (v)。
顶点的入度:在有向图中,顶点v的入度是指以该顶点为弧头的弧的数目,记为ID (v);
顶点的出度:在有向图中,顶点v的出度是指以该顶点为弧尾的弧的数目,记为OD (v)。
权:是指对边赋予的有意义的数值量。
网:边上带权的图,也称网图。
生成树:n个顶点的连通图G的生成树是包含G中全部顶点的一个极小连通子图。
生成森林:在非连通图中,由每个连通分量都可以得到一棵生成树,这些连通分量的生成树就组成了一个非连通图的生成森林。
3.不同结构中逻辑关系的对比
在线性结构中,数据元素之间仅具有线性关系;
在树结构中,结点之间具有层次关系;
在图结构中,任意两个顶点之间都可能有关系。
在线性结构中,元素之间的关系为前驱和后继;
在树结构中,结点之间的关系为双亲和孩子;
在图结构中,顶点之间的关系为邻接。
4.图的遍历操作
图的遍历是在从图中某一顶点出发,对图中所有顶点访问一次且仅访问一次。
深度优先遍历
⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
template <class DataType>
void MGraph<DataType> :: DFSTraverse(int v)
{
cout << vertex[v]; visited[v] = 1;
for (j = 0; j < vertexNum; j++)
if (arc[v][j] == 1 && visited[j] == 0) DFSTraverse( j );
}
广度优先遍历
⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。
template <class DataType>
void MGraph<DataType> :: BFSTraverse(int v)
{
front = rear = -1; //初始化顺序队列
cout << vertex[v]; visited[v] = 1; Q[++rear] = v;
while (front != rear) //当队列非空时
{
v = Q[++front]; //将队头元素出队并送到v中
for (j = 0; j < vertexNum; j++)
if (arc[v][j] == 1 && visited[j] == 0 ) {
cout << vertex[j]; visited[j] = 1; Q[++rear] = j;
}
}
}
二. 图的存储结构及实现
1. 邻接矩阵
基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
2. 邻接矩阵存储无向图的类
const int MaxSize=10;
template <class DataType>
class Mgraph
{
public:
MGraph(DataType a[ ], int n, int e );
~MGraph( )
void DFSTraverse(int v);
void BFSTraverse(int v);
private:
DataType vertex[MaxSize];
int arc[MaxSize][MaxSize];
int vertexNum, arcNum;
};
邻接矩阵构造函数算法
template <class DataType>
MGraph<DataType> :: MGraph(DataType a[ ], int n, int e)
{
vertexNum = n; arcNum = e;
for (i = 0; i < vertexNum; i++)
vertex[i] = a[i];
for (i = 0; i < vertexNum; i++) //初始化邻接矩阵
for (j = 0; j < vertexNum; j++)
arc[i][j] = 0;
for (k = 0; k < arcNum; k++) //依次输入每一条边
{
cin >> i >> j; //输入边依附的两个顶点的编号
arc[i][j] = 1; arc[j][i] = 1; //置有边标志
}
}
3. 邻接表
邻接表存储的基本思想:对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
邻接表存储有向图的类
const int MaxSize = 10; //图的最大顶点数
template <class DataType>
class ALGraph
{
public:
ALGraph(DataType a[ ], int n, int e);
~ALGraph;
void DFSTraverse(int v);
void BFSTraverse(int v);
private:
VertexNode adjlist[MaxSize];
int vertexNum, arcNum;
};
三. 最小生成树
生成树的代价:设G = (V, E)是一个无向连通网,生成树上各边的权值之和称为该生成树的代价。
最小生成树:在图G所有生成树中,代价最小的生成树称为最小生成树。
1. Prim算法
1. 初始化:U = {v0}; TE={ };
2. 重复下述操作直到U = V:
2.1 在E中寻找最短边(u,v),且满足u∈U,v∈V-U;
2.2 U = U + {v};
2.3 TE = TE + {(u,v)};
2.Kruskal算法
1. 初始化:U=V;TE={ };
2. 重复下述操作直到T中的连通分量个数为1:
2.1 在E中寻找最短边(u,v);
2.2 如果顶点u、v位于T的两个不同连通分量,则
2.2.1 将边(u,v)并入TE;
2.2.2 将这两个连通分量合为一个;
2.3 标记边(u,v),使得(u,v)不参加后续最短边的选取;
3.Dijkstra算法
1. 初始化数组dist、path和s;
2. while (s中的元素个数<n)
2.1 在dist[n]中求最小值,其下标为k;
2.2 输出dist[j]和path[j];
2.3 修改数组dist和path;
2.4 将顶点vk添加到数组s中;
4.Floyd算法
void Floyd(MGraph G)
{
for (i=0; i<G.vertexNum; i++)
for (j=0; j<G.vertexNum; j++)
{
dist[i][j]=G.arc[i][j];
if (dist[i][j]!=∞)
path[i][j]=G.vertex[i]+G.vertex[j];
else path[i][j]="";
}
for (k=0; k<G.vertexNum; k++)
for (i=0; i<G.vertexNum; i++)
for (j=0; j<G.vertexNum; j++)
if (dist[i][k]+dist[k][j]<dist[i][j]) {
dist[i][j]=dist[i][k]+dist[k][j];
path[i][j]=path[i][k]+path[k][j];
}
}
5. AOV网:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,称这样的有向图为顶点表示活动的网,简称AOV网。
AOV网特点:
1.AOV网中的弧表示活动之间存在的某种制约关系
2.AOV网中不能出现回路
拓扑排序:对一个有向图构造拓扑序列的过程称为拓扑排序 。