写程序时,需要一定的结构存储图,本文将介绍两种图常用的存储结构:邻接矩阵和邻接表。
邻接矩阵
邻接矩阵是通过二维数组对图中边的信息(包括边依附的两个顶点以及边的权值)进行存储。
邻接矩阵数组的大小取决于图中顶点的数量。
准备工作
使用邻接矩阵前,需要开一个一维数组数组,以存储各个顶点的数据(数组的编号与邻接矩阵中顶点的编号一一对应)
构造函数
邻接矩阵建图的构造函数要完成的三件事:
1.存储图的基本信息(顶点数、边数)
2.存顶点
3.存边(构造邻接矩阵)
MGraph(DateType a[],int n,int e){
vertxNum=n,EdgeNum=e; //1.顶点数、边数传入
int i,j,k,w; //i,j记录顶点;k记录边
for(i=0;i<vertxNum;i++) vertx[i]=a[i]; //2.顶点数组录入数据
for(i=0;i<vertxNum;i++) //3.初始化邻接矩阵
for(j=0;j<vertxNum;j++)
edge[i][j]=0;
for(k=0;k<EdgeNum;k++){
cout<<"输入边依附的两个顶点的编号,以及边上的权值:";
cin >> i >> j >> w; //4.输入边依附的两个顶点的编号及边的权值
edge[i][j]=w;
/* edge[j][i]=w; */ //若为无向图,可以加本行;若为有向图,不可加入本行!
}
}
析构函数
邻接矩阵本质上是数组,为静态存储,所以类中无需手动写析构函数,使用默认析构函数即可!
~MGraph(){}
邻接表
邻接表,存储与树的孩子链表示法相类似,是顺序分配和链式分配相结合的存储结构。
如果顶点表中的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。
准备工作
使用邻接表存储图,需要构造两个结构体:
1.邻接边表
struct EdgeNode //邻接边表结点
{
int adjvex; //邻接点域
EdgeNode *next; //指向邻接表的指针
};
2.顶点表
struct VertexNode //顶点表结点
{
int in; //入度域
DataType vertex; //顶点数据
EdgeNode *firstEdge; //指向邻接表的指针
};
构造函数
使用面向对象完成邻接表,一般情况需要在构造函数中完成两件事:
1.存储顶点信息到顶点表;
2.存边权信息到邻接表 。
ALGraph(char a[],int n,int e){
//邻接表的构造干两件事:1.存储顶点信息到顶点表;2.存边权信息到邻接表
vertexNum=n, edgeNum=e;
int i,j,k;
//存顶点
for(i=0;i<vertxNum;i++){
adjlist[i].vertx=a[i];
adjlist[i].firstEdge=NULL;
}
EdgeNode *p=NUll;
//存边
for(k=0;k<EdgeNum;k++){
cin>>i>>j; //输入顶点i编号和其邻接点j编号
//头插法将邻接点的信息存入表中,此时顶点表的每个顶点就相当于单链表中的头结点first,firstEdge等价于单链表头结点中的next域(first->next)
p=new EdgeNode; //工作指针p,用于操作新加入的结点
p->adjlist=j; //邻接表记录邻接点
p->next=adjlist[i].firstEdge; //插入点的下一位置指向头结点下一位置(插入元素置于邻接表表头)
adjlist[i].firstEdge=p; //头结点指向新插入点
//如此循环,输入的邻接点信息就可以依次插入到邻接表的头部
}
析构函数
由于邻接表本质是链表,存储数据时为动态存储,需要手动析构!
因此,析构函数要完成的工作是循环删除每个结点!!
~ALGraph(){
EdgeNode *p,*q; //工作指针p ,临时指针q(暂存被删除元素)
p=NULL,q=NULL;
for(int i=0;i<vertxNum;i++){
p=adjlist[i].firstEdge; //工作指针,用于向后移动
q=adjlist[i].firstEdge; //暂存被删元素
while(p!=NULL){
p=p->next;
delete q;
q=p;
}
}
}