图——概念,存储结构
基本概念
多对多的逻辑结构
-
我们假设ABCDEFG是7个电话,之间的连线表示修有通信线路
-
电话就是图的顶点Vi∈V,通信线路是边ei∈E,G={V,E}就是一个图
-
只要两个电话间有线路,就可以相互通话 即 无向图
-
电话(顶点)连接的线路(边)数量:度
-
ABCDE和GF之间消息无法传递:不连通
-
ABCDE和GF是两个联通分量
-
假设ABCDE是五个电话,之间的连线表示修有通信线路,数字表示该线路的电话费
-
不同通信线路上的电话费不同:加权图
-
假设ABCDE是五个城市,带箭头连线表示该方向上有航班运营
-
例如航班A->D只能支持A飞往D,边是单向的=>有向图
-
飞来某地的航班数量 :入度
-
从某地起飞的航班数量:出度
- 在一个无向图中,所有顶点的度数之和为边数量的2倍
- 在一个有向图中,所有顶点的出度之和==所有顶点的入度之和
存储结构
邻接矩阵(无向图)
考虑到图是由顶点和边或弧两部分组成,合在一 起比较困难,就很自然地考虑到分为两个结构来分别存储。
顶点因为不区分大小、主次,所以用一个一维数组来存储是很不错的选择。
而边或弧由于是顶点与顶点之问的关系, 一维数组肯定就犒不定了,我们不妨考虑用一个二维数组来存储。
图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。
一个一维数组存储图中顶点信息,一个二维数组(称为矩阵)存储图中边或弧的信息。
对称矩阵:所谓对称矩阵就是n阶矩阵的元满足a[i][j]=a[j]i。
即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的。
- 要判定任意两顶点是否有边无边就非常容易了;
- 要知道个某个顶点的度,其实就是这个顶点Vi在邻接矩阵中第i行(或第i列)的元素之和;
- 求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接接点。
邻接矩阵(有向图)
矩阵并不对称,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1没有弧,因此arc[0][1]=0
另外有向图是有讲究的,要考虑入度和出度。
邻接矩阵(网)
每条边上带有权的图叫网。
网即是有权值的图。
- 网中数组array中元素表示两个顶点之间的关系。
- 如果array[ i ] [ j ]=weight表示顶点 i 到(和)顶点 j 之间有一条边,其权值为weight。
- 如果array[ i ] [ j ]=正无穷表示顶点 i 到(和)顶点 j 之间没有边。
- 如果i和j是同一顶点,array[ i ] [ j ]=0。当然也可以为正无穷,灵活变化。
#include <stdio.h>
#define Maxlnt 32767 //表示极大值,即 ∞
#define MVNum 100 //最大顶点数
typedef char VerTexType; //设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
typedef struct{
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum; //图的当前点数和边数
}AMGraph;
邻接表(无向图)
对于边数相对顶点较少的图,邻接矩阵是对存储空间的巨大浪费。
因此我们可以考虑另外一种存储结构方式,例如把数组与链表结合一起来存储,这种方式在图结构也适用,我们称为邻接表。
邻接表的处理方法是这样:
- 图中顶点用一个一维数组存储。
- 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不确定,所以我们选择用单链表存储。
代码实现
#include <stdio.h>
#include<stdlib.h>
#define MaxVertices 100
typedef struct EdgeNode EdgeNode;
typedef struct EdgeNode //边表
{
int adjvex;
EdgeNode *next;
}EdgeNode;
typedef struct //顶点表
{
int vertex;
EdgeNode *edgenext
}VertexNode;
typedef VertexNode AdjList[MaxVertices];///顶点表数组
typedef struct
{
AdjList adjlist;
int n,e;
}AdjMatrix;
void CreateGraph(AdjMatrix *G)//生成邻接表
{
int i,j,k;
EdgeNode *s;
printf("输入顶点数和边数(中间以空格分开):");
scanf("%d%d",&G->n,&G->e);
printf("建立顶点表\n");
for ( i = 0; i < G->n; i++)
{
getchar();
printf("请输入第%d个顶点的信息:",i+1);
G->adjlist[i].vertex=getchar();
G->adjlist[i].edgenext=NULL;
}
//前插法
printf("建立边表\n");
for ( k = 0; k < G->e; k++)
{
printf("输入有连接的顶点序号(中间以空格分开):");
scanf("%d%d",&i,&j);
i -=1;j -=1;
//对于直接相连的进行编入(即对输入“0 1”时,在0对应的边表中编入1)
s=(EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex=j;
s->next=G->adjlist[i].edgenext;
G->adjlist[i].edgenext=s;
//对于间接相连的进行编入(即对输入“0 1”时,在1对应的边表中编入0)
s=(EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex=i;
s->next=G->adjlist[j].edgenext;
G->adjlist[j].edgenext=s;
}
}
void ShowGraph(AdjMatrix *G)
{
int i;
for ( i = 0; i < G->n; i++)
{
printf("%d->",i+1);
while (1)
{
if(G->adjlist[i].edgenext==NULL)
{
printf("^");
break;
}
printf("%d->",G->adjlist[i].edgenext->adjvex+1);
G->adjlist[i].edgenext=G->adjlist[i].edgenext->next;
}
printf("\n");
}
}
int main()
{
AdjMatrix *G=(AdjMatrix*)malloc(sizeof(AdjMatrix));
CreateGraph(G);
ShowGraph(G);
return 0;
}