图的存储结构
1.邻接矩阵表示法
邻接矩阵可以表示顶点之间的关系,可以借助二维数组表示,对于无向图若两点之间的边存在,则该位置的值为1,反之则为0;对于有向图还需要考虑入点和出点的问题,若一条边从 i 点出发进入 j 点,则 a [ i ][ j ]=1,而 a [ j ][ i ]仍为0。
采用邻接矩阵表示法创建图时,用一个二维数组储存边,再用一个一维数组储存点的名称和边的权值即可。下面以创建无向网为例
#include <iostream>
#include <string>
using namespace std;
//无向网的创建
#define maxint 32767
#define mvnum 100
typedef struct
{
char vexs[mvnum];
int arcs[mvnum][mvnum];
int vexnum,arcnum;//图的总顶点数,总边数
}AMGraph;
int Locate(char a,char b[]);
int CreateUDN(AMGraph &G)
{
char a,b;
int w;
int j,k;
//cout<<"请输入图的总顶点数和总边数"<<endl;
cin>>G.vexnum>>G.arcnum;
//cout<<"请输入图的顶点信息"<<endl;
for(int i=0;i<G.vexnum;i++)
cin>>G.vexs[i];//读入点的信息
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
G.arcs[i][j]=maxint;//初始化图使所有边均不存在(值为无穷大)
}
}
for(int i=0;i<G.arcnum;i++)
{
//cout<<"请输入一条边链接的两个顶点和该边权值"<<endl;
cin>>a>>b>>w;
j=Locate(a,G.vexs),k=Locate(b,G.vexs);
G.arcs[j][k]=w;
G.arcs[k][j]=w;
}
return 1;
}
int Locate(char a,char b[])
{
for(int i=0;i<mvnum;i++)
{
if(b[i]==a)
return i;
}
}
int main()
{
AMGraph G;
CreateUDN(G);
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
cout<<G.arcs[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
2.邻接表表示法
图中每个顶点对应一个结点,所有顶点用一个一维数组或一个单链表存储,每个顶点的邻接点构成一个线性表,利用单链表来存储。
邻接表在建立时若顶点单链表中储存的是顶点指向的点则该表为正邻接表,便于计算顶点的出度;
若储存的是指向顶点的点则该表为逆邻接表,便于计算顶点的入度。
对于带权的网图,在顶点中加入数据域即可。
#include <iostream>
#include <string>
using namespace std;
//无向网的创建(邻接表法)
#define mvnum 100
typedef struct Arcnode
{
int weight;//边的权值
struct Arcnode *next;//指向链表中下一个结点(下一条边)
int p;//该边邻接的点
}Arcnode;//边结点
typedef struct
{
char name;//顶点名
Arcnode *firtstarc;//指向邻接的第一条边
}Adjlist[mvnum];//存放顶点的一维数组
typedef struct
{
Adjlist vertices;//创建存放顶点的一维数组
int vexnum,arcnum;//图的总顶点数,总边数
}ALGraph;
int Locate(char a,ALGraph G)
{
for(int i=0;i<mvnum;i++)
{
if(G.vertices[i].name==a)
return i;
}
}
int CreateUDN(ALGraph &G)
{
char a,b;
int w;
int j,k;
cout<<"请输入图的总顶点数和总边数"<<endl;
cin>>G.vexnum>>G.arcnum;
cout<<"请输入图的顶点信息"<<endl;
for(int i=0;i<G.vexnum;i++)
{
cin>>G.vertices[i].name;//读入点的信息
G.vertices[i].firtstarc=NULL;//表头指针赋空值
}
for(int i=0;i<G.arcnum;i++)
{
cout<<"请输入一条边链接的两个顶点和该边权值"<<endl;
cin>>a>>b>>w;
j=Locate(a,G),k=Locate(b,G);
Arcnode *p1;
p1=new Arcnode;
p1->p=j;
p1->weight=w;
p1->next=G.vertices[j].firtstarc;
G.vertices[j].firtstarc=p1;//将数据插入链表
Arcnode *p2;
p2=new Arcnode;
p2->p=k;
p2->weight=w;
p2->next=G.vertices[k].firtstarc;
G.vertices[k].firtstarc=p2;//将数据插入链表
}
return 1;
}
int main()
{
ALGraph G;
CreateUDN(G);
Arcnode *p3;
p3=G.vertices[0].firtstarc;
while(p3!=NULL)
{
cout<<p3->p<<' '<<p3->weight<<endl;
p3=p3->next;
}
return 0;
}
邻接表法的缺点在于不便于判断两点之间是否有边,以及某点的入度和出度难以同时判断,下面的十字链表较好的解决了这一问题。
3.十字链表法
构造有向图时可以对邻接表法进行优化,使可以同时得到某点的入度和出度,这就是十字链表法,也可以看做是邻接表和逆邻接表的结合。不同的地方在于
#define mvnum 100
typedef struct Arcnode
{
int weight;//边的权值
struct Arcnode *hlink,*tlink;//生成两个链表
int tailvex,headvex;//该边邻接的两个点
}Arcnode;//对于边结点,记录该边邻接的两个点和链表中弧头相同的下一条弧和弧尾相同的下一条弧
typedef struct
{
char name;//顶点名
Arcnode *firstin,*firstout;//指向该点的第一条入弧和第一条出弧
}Adjlist[mvnum];//存放顶点的一维数组
typedef struct
{
Adjlist vertices;//创建存放顶点的一维数组
int vexnum,arcnum;//图的总顶点数,总边数
}ALGraph;
十字链表的时间复杂度与邻接表的相同,在有向图中十字链表的应用更加广泛
4.邻接多重表
邻接多重表的结构与十字链表类似,便于找到同一条边的两个结点,即结点依附于边存在,实现的代码如下
#define mvnum 100
typedef struct Arcnode
{
int weight;//边的权值
int mark;//访问标记
struct Arcnode *ilink,*jlink;//指向依附于这两个顶点的下一条边
int ivex,jvex;//该边依附的两个顶点位置
}Arcnode;//边结点
typedef struct
{
char name;//顶点名
Arcnode *firtstarc;//指向第一条依附于该顶点的边
}Adjlist[mvnum];//存放顶点的一维数组
typedef struct
{
Adjlist vertices;//创建存放顶点的一维数组
int vexnum,arcnum;//图的总顶点数,总边数
}ALGraph;
|