零、说明
- 本文中,V代表Vertex,即顶点,E代表Edge,即边。什么Vnum,Enum,firstE都按照这个来理解。
- 原理我是按《大话数据结构》来的,我也解释不好这个原理,就不献丑了
- 存储方式还远不止这些,有个好像叫前向星,我现在还不会(流下了菜鸡的眼泪)
- 本文有很多地方不严谨,后续待修改
一、邻接矩阵
- 数据类型
typedef struct
{
char Vertex[MAXSIZE];
int Edge[MAXSIZE][MAXSIZE];
int Vnum,Enum;
}MGraph;//叫MGraph是因为矩阵(Matrix)
包含顶点表和矩阵两个部分,矩阵的每一个元素都代表某点(行)到某点(列)是否有边存在
2. 初始化
void CreateMGraph(MGraph *M)
{
int i,j,start,end;
for(i=0;i<MAXSIZE;i++)
for(j=0;j<MAXSIZE;j++)
{
M->Edge[i][j]=INFINITY;//提前define为65535
}
cout<<"请输入图的点数和边数:";
cin>>M->Vnum>>M->Enum;
cout<<"请输入各点数据:";
for (i=0;i<M->Vnum;i++)
{
cin>>M->Vertex[i];
}
cout<<"请按'始边 终边 权值'的顺序输入图的边(只支持数字序号,且从零开始):\n";
for (i=0;j<M->Enum;i++)
{
cin>>start;
cin>>end;
cin>>M->Edge[start][end];
M->Edge[end][start]=M->Edge[start][end]; //这一行
}
}
说明几点:
计数从零开始,所以就比如0号点到3号点有边
用在无向图中,如果想用在有向图,上面示意的那一行删掉即可
二、邻接表
0.起因:邻接矩阵直观是直观,但是空间浪费挺多的,联想到前面用链式结构节省空间,用链式结构存储图彳亍不彳亍?
彳亍!
- 数据类型(List)
分三层:首先定义边节点,然后定义图中的节点,节点中有指向一个边的指针(所以不是第一个定义它),最后定义整个图,就是点的数组
typedef struct LEnode
{
int Vdata;//连接的点序号
int weight;//该边权值
struct LEnode *next;//同节点的下一条边
}LEnode;
typedef struct LVnode
{
int data;
LEnode *firstE;
}LVnode,lGraph[MAXSIZE];
typedef struct
{
int Vnum,Enum;
lGraph G;
}LGraph;
- 初始化
很容易理解,就是新建一个边,输入,按头插法连接到点上
void CreateLGraph(LGraph *L)
{
int i,start,end,weight;
LEnode *e;
cout<<"请输入顶点数和边数:";
cin>>L->Vnum>>L->Enum;
cout<<"请输入顶点数据:";
for (i=0;i<L->Vnum;i++)
{
cin>>L->G[i].data;
L->G[i].firstE=NULL;
}
cout<<"请输入边:按'始边 终边 权值'的格式\n";
//头插法
for(i=0;i<L->Enum;i++)//注意,这个是无向图,想用有向图就修改一下
{
cin>>start>>end>>weight;
e=new LEnode;
e->Vdata=end;
e->weight=weight;
e->next=L->G[start].firstE;
L->G[start].firstE=e;
e=new LEnode;
e->Vdata=start;
e->weight=weight;
e->next=L->G[end].firstE;
L->G[end].firstE=e;
}
}
三、十字链表(邻接多重表)
A.有向的称为十字链表
- 数据类型(Cross)
typedef struct CEnode1
{
int weight;
int tailV,headV;
struct CEnode1 *headnext,*tailnext;//分别指向弧头、弧尾相同的下一条边
}CEnode1;
typedef struct CVnode1
{
int data;
CEnode1 *firstIn,*firstOut;
//第一条入/出这个点的边(按输入顺序,所以没有严格正确的先后之分,
//只是代表性地用一下'第一条'这个词罢了)
}CVnode1,cGraph1[MAXSIZE];
typedef struct
{
int Vnum,Enum;
cGraph1 G;
}CGraph1;
- 初始化
void CreateCGraph1(CGraph1 *C)//有向图
{
int i;
CEnode1 *e;
cout<<"请输入顶点数和边数:";
cin>>C->Vnum>>C->Enum;
cout<<"请输入顶点数据:";
for(i=0;i<C->Vnum;i++)
{
cin>>C->G[i].data;
C->G[i].firstIn=NULL;
C->G[i].firstOut=NULL;
}
cout<<"请输入边:按'始边 终边 权值'的格式\n";
for(i=0;i<C->Enum;i++)//重点在这个循环,思路和邻接表类似,原理大概就是每条边都存储着下一条相同始/终点的边
{
e=new CEnode1;
cin>>e->tailV>>e->headV>>e->weight;
e->tailnext=C->G[e->tailV].firstOut;
C->G[e->tailV].firstOut=e;
e->headnext=C->G[e->headV].firstIn;
C->G[e->headV].firstIn=e;
}
}
B. 无向的称为邻接多重表
- 数据类型
说实话,无向的数据类型和有向的挺像,不过简洁了许多
typedef struct CEnode2
{
int weight;
int Vi,Vj;//这条边两端的顶点
struct CEnode2 *Vinext,*Vjnext;//Vi,Vj连接的下一条边
}CEnode2;
typedef struct CVnode2
{
int data;
CEnode2 *firstE;
}CVnode2,cGraph2[MAXSIZE];
typedef struct
{
int Vnum,Enum;
cGraph2 G;
}CGraph2;
- 初始化
void CreateCGraph2(CGraph2 *C)//无向图
{
int i;
CEnode2 *e;
cout<<"请输入顶点数和边数:";
cin>>C->Vnum>>C->Enum;
cout<<"请输入顶点数据:";
for(i=0;i<C->Vnum;i++)
{
cin>>C->G[i].data;
C->G[i].firstE=NULL;
}
cout<<"请输入边:按'始边 终边 权值'的格式\n";
for(i=0;i<C->Enum;i++)
{
e=new CEnode2;
cin>>e->Vi>>e->Vj>>e->weight;
e->Vinext=C->G[e->Vi].firstE;
C->G[e->Vi].firstE=e;
e->Vjnext=C->G[e->Vj].firstE;
C->G[e->Vj].firstE=e;
}
}
四、边集数组
- 大概类似邻接矩阵的三元组存储法?(矩阵还没写。。。)
- 数据类型
typedef struct
{
int Vnum,Enum;
int V[MAXSIZE];
int E[MAXSIZE][3];
}EGraph;
- 初始化
没什么好说的
void CreateEGraph(EGraph *E)
{
int i;
cout<<"请输入顶点数和边数:";
cin>>E->Vnum>>E->Enum;
cout<<"请输入顶点数据:";
for(i=0;i<E->Vnum;i++)
{
cin>>E->V[i];
}
cout<<"请输入边:按'始边 终边 权值'的格式\n";
for(i=0;i<E->Enum;i++)
{
cin>>E->E[i][0]>>E->E[i][1]>>E->E[i][2];
}
}