图由结点的有穷集合V(vertex)和边的集合E(edge)组成。为与树形 结构进行区别,在图结构仲裁杭长将结点称为顶点,边 时顶点的有序偶对。若两个顶点之间存在一条边, 则表示这两个顶点具有相邻关系。
有向完全图和无向完全图
若有向图中有n个顶点,则最多有n(n-1)条边,(将具有n(n-1)条边的有向图称为有向完全图)。若无向图中有n个顶点,则最多有n(n-1)/2条边,(将具有n(n-1)/2条边的无向图称为无向完全图)
简单路径:序列中顶点不重复出现的路径称为简单路径
回路:若一条路径中第一个顶点和最后一个顶点相同,则这条路径是一条回路。
连通、连通图和连通分量
无向图中,若从一个顶点到另外一个顶点有路径,则称这两个顶点连通。
如果图中任意两个顶点之间都连通,则称该图为连通图;否则,图中的极大连通子图称为连通分量
强连通图和强连通分量
有相图中,若从一点顶点到另外一个顶点有路径,则称这两个顶点连通。
如果对于每一对顶点, 两顶点之间都有互相指向的路径,则称该图为强连通图;否则将其中的极大强连通子图称为强连通分量
图的存储结构
1) 邻接矩阵
邻接矩阵是图的顺序存储结构, 由邻接矩阵的 行数或列数可知图中的顶点数。对于无向图,邻接矩阵是对称的,矩阵中“1”的个数为图中总边数的两倍,矩阵中第i行或第i列的元素之和即为顶点i的度。对于有向图,矩阵中“1”的个数为图的边数,矩阵中第i行的元素之和即为顶点i的出度,第j列元素之和即为顶点j的入度。
typedef struct
{
int no; //顶点编号
}VertexType; //顶点类型
typerdef struct //图的定义
{
int edges[maxSize][maxsize]; //邻接矩阵定义(若是有权图,则将int改为float)
int n,e; //分别存放顶点数和边数
VertexType vex[maxsize]; //存放结点信息
}MGraph; //图的邻接矩阵类型
2)邻接表
邻接表是图的一种链式存储结构。对图中的每个顶点i建立一个单链表,每个单链表的第一个结点存放有关顶点的信息,把这个结点看做链表的表头,其余结点存放有关边的信息。
因此邻接表由单链表的表头形成的顶点表和单链表其余结点形成的边表两部分组成。一般顶点表存放顶点信息和指向第一个边结点指针,边表结点存放与当前顶点相邻接顶点的序号和指向下一个边结点的指针。
typedef struct ArcNode //边表结点结构体定义
{
int adjvex; //该边所指向的结点的位置
struct AcrNode *nextarc; //指向下一条边的指针
int info;
}ArcNode;
typedef stuct //顶点表结构体定义
{
char data; //顶点信息
ArcNode *firstarc; //指向第一条边的指针
}VNode;
typedef struct
{
VNode adjlist[maxSize]; //邻接表
int n,e; //顶点数和边数
}AGraph; //图的邻接表类型
图的深度优先搜索遍历(DFS)
int visit[maxSize];
/*v是起点编号,visit[]是一个全局数组,作为顶点的访问标记,初始时所有元素均为0,表示所有顶点都未被访
问。因图中可能存在回路,当前经过的顶点将来还有可能再次经过,所以要对每一个顶点进行标记,以免重复访问
*/
void DFS(AGraph *G,int v)
{
ArcNode *p; //辅助指针
visit[v]=1; //置已访问标记
Visit(v); //代表一类访问顶点v的操作
p=G->adjlist[v].firstarc; //p指向顶点v的第一条边
while(p!=NULL)
{
if (visit[p->adjvex]==0) //若顶点未访问,则递归访问它
DFS(G,p->adjvex); //递归
p=p->nextarc; //p指向顶点v的下一条边的终点
}
}
将图的深度优先搜索遍历过程中经历的边保留,其余边删除,就会形成一棵树,称为深度优先搜索生成树
图的广度优先搜索遍历(BFS)
void BFS(AGraph *G,int v,int visit[maxSize])
{
ArcNode *p;
int queue[maxSize],front=rear=0;
int j;
Visit(v);
visit[v]=1;
rear=(rear+1)%maxSize;
queue[rear]=v;
while(front!=rear)
{
front=(front+1)%maxSize;
j=queue[front];
p=G->adjlist[j].firstarc;
while(p!=NULL)
{
if(visit[p->adjvex]==0)
{
Visit(p->adjvex);
visit[p->adjvex]=1;
rear=(rear+1)%maxSize;
queue[rear]=p->adjvex;
}
p=p->nextarc;
}
}
}
以上两种遍历方法是针对连通图的,对于非连通图,只需将上述遍历函数放在一个循环中,循环用来检测图中的每一个顶点,如果当前顶点没有被访问,则调用上述函数从这个顶点遍历,否则什么也不做。