图
定义: 由顶点的有穷非空集合和顶点之间的边的集合组成,表示为:G(V,E),其中,G表示一个图,V表示顶点集合,E表示边的集合
注:顶点集合不能为空,边集为空
各种图定义
-
无向边:若顶点Vi到Vj之间的边没有方向,则称这条边为无向边(Edge),可用无序偶对表示为:(Vi,Vj)
若图中任意两个顶点之间的边都是无向边,则称该图为无向图.
G = (V,{E})
V = {A,B,C,D}
E = {(A,B),(A,C),(B,C),(C,D),(D,A)}
-
有向边:若顶点Vi到Vj的边有方向,则称此条边为有向边,也成为弧(Arc),可用有序偶对表示为:<Vi,Vj>,
Vi为弧尾(Tail),Vj为弧头(Head).
若图中任意两个顶点之间的边都是有向边,则称此图为有向图.
G = (V,{E})V = {A,B,C,D}
E = {<A,D>,<B,A>,<C,A>,<B,C>}
-
简单图:在图中不存在顶点到自身的边,且同一条边不重复出现,则称这样的图为简单图
-
无向完全图:在无向图中,任意两个顶点之间都有边,称此图为无向完全图.
含有N个顶点的无向完全图拥有的边数为:
N ∗ ( N − 1 ) / 2 N*(N-1)/2 N∗(N−1)/2
所以N个顶点的无向图最多拥有N*(N-1)/2条边
-
有向完全图:在有向边中,任意两个顶点之间都存在方向互为相反的两条弧,则称此图为有向完全图,
含有N个顶点的有向完全图拥有的边数为:
N ∗ ( N − 1 ) N*(N-1) N∗(N−1)
所以N个顶点的有向图最多拥有N*(N-1)/2条边
-
稠密图与稀疏图:对于N个顶点和M条边的图,M远小于N^2的图成为稀疏图,M较大的图称为稠密图(边或弧多称为稠密图,反之称为稀疏图).
-
权值:与图的边或弧相关的数叫做权,带权的图称为网.
-
子图:存在两个图 G = (V,{E}) 和 G’ = (V’,{E’}) 若V’ 包含于 V,E’ 包含于 E.则称G’为G的子图(Subgraph)
图的顶点与边间关系
-
对于无向图G = (V,{E}),若(V,V’) 属于 E则称V和V’互为邻接点(Adjacent),即V和V’相邻接
V的度(Degree)是与V相连接的边的数目记为:TD(V).
边数(e)是各顶点度数和的一半即
e = 1 / 2 ∗ ∑ ( i = 1 , n ) T D ( V i ) e = 1/2*\sum (i = 1,n)TD(Vi) e=1/2∗∑(i=1,n)TD(Vi) -
对于有向图G = (V,{E}),以顶点V为头的弧的数目称为V的入度(InDegree),记作ID(V)
以顶点V为尾的弧的数目称为V的出度(OutDegree),记作OD(V).
顶点D的度为TD(V) = ID(V) + OD(V)
所以边数(e)为所有顶点的入度和或者出度和:
e = 1 / 2 ∗ ∑ ( i = 1 , n ) I D ( V i ) = ∑ ( i = 1 , n ) O D ( V i ) e = 1/2* \sum(i = 1,n)ID(Vi) = \sum(i = 1,n)OD(Vi) e=1/2∗∑(i=1,n)ID(Vi)=∑(i=1,n)OD(Vi) -
无向图G = (V,{E})中的路径(Path)是一个顶点序列(Vi,Vj),其中(Vi,Vj) 属于 E.
路径的长度是路径上的边或弧的数目
-
第一个顶点到最后一个顶点相同的路径称为回路或者环(Cycle).序列中顶点不重复出现的路径称为简单路径,除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路或者简单环.
连通图的相关术语
-
连通图:在无向图G中,如果从顶点v到顶点v’有路径,则称v和v’是连通的。如果对于图中任意两个顶点Vi, Vj∈E, Vi和Vj都是连通的,则称G是连通图(ConnectedGraph).
例子:
此图的连通分量为:
和
-
一个连通图的生成树是一个极小的连通子图,它含有全部N个顶点,拥有构成一颗树的N-1条边.
所以:一个图有N个顶点和小于N-1条边,一定不是连通图,多余N-1条边,则一定有环.
图的存储结构
-
邻接矩阵(AdjGraph):使用两个矩阵来表示图:一维数组Vex存储顶点,二维数组map存储边或弧的信息
可以存储边数较多的稠密图
/*构建邻接矩阵*/ typedef char VexType; //顶点的数据类型 typedef int EdgeType; //边上的权值类型 #define MAXVEX 100 //最大顶点数 #define INF 9999 //表示无穷大 typedef struct { VexType vexs[MAXVEX]; //顶点表 EdgeType map[MAXVEX][MAXVEX]; //邻接矩阵 int vexnum,edgenum; //顶点数和边数 } AdjGraph; //邻接矩阵 void InitAdjGraph(AdjGraph* M)//初始化邻接矩阵 { printf("输入顶点数和边数:"); scanf("%d%d",&(M->vexnum),&(M->edgenum)); for(int i = 0; i<M->vexnum; i++) { for(int j = 0; j<M->vexnum; j++) { if(i == j) M->map[i][j] = 0;//自身不能到自身 else M->map[i][j] = INF; } } fflush(stdin); printf("输入顶点:");//构建顶点表 for(int i = 0; i<M->vexnum; i++) scanf("%c",&(M->vexs[i])); fflush(stdin); for(int i = 0; i<M->edgenum; i++) //构建邻接矩阵 { printf("输入边(Vi,Vj)的下标i,j和该边权值w:"); int l,r; EdgeType w; scanf("%d%d%d",&l,&r,&w); M->map[l][r] = w; // M->map[r][l] = w;//有向图 } } void Print(AdjGraph* M)//展示 { printf("顶点表为\n"); for(int i = 0; i<M->vexnum; i++) printf("%c ",M->vexs[i]); printf("\n邻接矩阵为\n"); for(int i = 0; i<M->vexnum; i++) { for(int j = 0; j<M->vexnum; j++) printf("%-4d ",M->map[i][j]); putchar('\n'); } } int main() { AdjGraph *M = (AdjGraph*)malloc(sizeof(AdjGraph)); InitAdjGraph(M); Print(M); return 0; }
效果图:
-
邻接表(AdjList):采用数组和链表相结合的方式
可以存储边较少的图
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef char VexType; //顶点的数据类型
typedef int EdgeType; //边上的权值类型
#define MAXVEX 100 //最大顶点数
#define INF 9999//表示不连通
typedef struct EdgeNode
{
int index; //节点下标
EdgeType weight; //权值
struct EdgeNode* next;
} EdgeNode; //边表节点
typedef struct
{
VexType vex; //顶点
EdgeNode *firstedge; //指向第一个边节点
} VexNode; //顶点表节点
typedef struct
{
VexNode list[MAXVEX]; //顶点表节点数组
int vexnum,egdenum; //顶点数和边数
} AdjList; //邻接表
void InitAdjList(AdjList* M)//初始化邻接表
{
printf("输入顶点数和边数:");
scanf("%d%d",&(M->vexnum),&(M->egdenum));
fflush(stdin);
printf("输入顶点:");
for(int i = 0; i<M->vexnum; i++)
{
scanf("%c",&(M->list[i].vex));
M->list[i].firstedge = NULL; //将边表制空
}
fflush(stdin);
for(int i = 0; i<M->egdenum; i++)//输入边
{
printf("输入边(Vi,Vj)的下标i,j和该边权值w:");
int l,r;
EdgeType w;
scanf("%d%d%d",&l,&r,&w);
EdgeNode* edge;
edge = (EdgeNode*)malloc(sizeof(EdgeNode));//头插法
edge->index = r;//记录下标
edge->weight = w;
edge->next = M->list[l].firstedge;
M->list[l].firstedge = edge;
// edge = (EdgeNode*)malloc(sizeof(EdgeNode));//无向图
// edge->index = l;
// edge->weight = w;
// edge->next = M->list[r].firstedge;
// M->list[r].firstedge = edge;
}
}
void Print(AdjList* M)//展示
{
for(int i = 0; i<M->vexnum; i++)
{
printf("%c:\n",M->list[i].vex);
EdgeNode *p = M->list[i].firstedge;
while(p)
{
printf("_%c -> %c = %d\n",M->list[i].vex,M->list[p->index].vex,p->weight);
p = p->next;
}
}
}
int main()
{
AdjList *M = (AdjList*