最近没学什么东西,但图基本的一些存储方式,马马虎虎懂了。
图的存储方式:
- 邻接矩阵
- 邻接表(逆邻接表)
- 十字链表
- 多重邻接表
为了突出重点,在下面给的例子中,有些的图会默认为无权图。
① 邻接矩阵
==基本想法==,就是用一个(n+1)*(n+1) 的数组,来表示每个顶点与其他顶点之间的关系。这里也可以用n*n 的数组
当图为无向图时,可以采用压缩矩阵的方式,即上三角,或下三角存储。
邻接矩阵这种存储方式适用于稠密图,而对于稀疏图则会浪费大量的内存空间。
一般的邻接矩阵
//这个是我第一次写邻接矩阵,几乎和参考教材上一样。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MaxNum 20
#define MaxValue -1
typedef struct{
char Vertex[MaxNum]; //保存顶点信息
int GType; //图的类型(0:无向图,1:有向图)
int VertexNum; //顶点的数量
int EdgeNum; //边的数量
int EdgeWeight[MaxNum][MaxNum]; //保存边的权
int isTrav[MaxNum]; //保存遍历标志
}GraphMatrix; //定义邻接矩阵图结构
void InitGraph(GraphMatrix *GM)
{
int i,j;
GM->GType=0;
printf("请输入图的定点个数:");
scanf("%d",&GM->VertexNum);
printf("请输入图的边的数量:");
scanf("%d",&GM->EdgeNum);
for(i=0;i<GM->VertexNum;i++)
{
for(j=0;j<GM->VertexNum;j++)
{
GM->EdgeWeight[i][j]=MaxValue;
}
//GM->isTrav[i]=0;
}
}
void CreateGraph(GraphMatrix *GM)
{
int i,j,k;
int weight; //权
char EstartV,EendV; //边的起始顶点
printf("输入图中各定点信息\n");
for(i=0;i<GM->VertexNum;i++) //输入顶点
{
printf("第%d个顶点:",i+1);
scanf(" %c",&GM->Vertex[i]); //保存到各定点数组元素中
}
printf("输入构成各边的顶点及权值:\n");
for(k=0;k<GM->EdgeNum;k++)
{
getchar();
printf("第%d条边:",k+1);
scanf("%c %c %d",&EstartV,&EendV,&weight);
for(i=0;EstartV!=GM->Vertex[i];i++); //在已有顶点中查找始点
for(j=0;EendV!=GM->Vertex[j];j++); //在已有定点中查找终点
GM->EdgeWeight[i][j]=weight;
if(GM->GType==0)
{
GM->EdgeWeight[j][i]=weight; //在对角位置保存权值
}
}
}
void OutGraph(GraphMatrix *GM) //显示图
{
int i,j;
for(j=0;j<GM->Vertex[j];j++){
printf("\t%c",GM->Vertex[j]); //在第一行输入顶点的信息
}
printf("\n");
for(i=0;i<GM->VertexNum;i++)
{
printf("%c",GM->Vertex[i]);
for(j=0;j<GM->VertexNum;j++)
{
if(GM->EdgeWeight[i][j]==MaxValue) //若权值表示无穷大
{
printf("\tZ"); //用z表示无穷大
}
else
{
printf("\t%d",GM->EdgeWeight[i][j]); //输出边的权值
}
}
printf("\n");
}
}
int main(){
GraphMatrix *GM=(GraphMatrix *)malloc(sizeof(GraphMatrix));
InitGraph(GM);
CreateGraph(GM);
OutGraph(GM);
ClearGraph(GM);
1
下面给出简易版的上三角矩阵推到图
下面给出代码
#include<stdio.h>
#include<string.h>
int main()
{
//数组元素个数=(1+j)*j/2 j为列下标,i为行下标
//这里默认为 3*3;
int b[6+1]; //数组从0开始标,所以加一位方便处理
int i,j,t=2,m;
memset(b,0,sizeof(b));
//默认为图有2条边
while(t-->0)
{
scanf("%d%d",&i,&j);
if(i>j)
{
m=(i-1)*i/2+(i-j+1);
b[m]=1;
}
else if(i<j) //图不允许一个顶点自己有边,通向自己。
{
m=(j-1)*j/2+(j-i+1);
b[m]=1;
}
}
printf(" 1 2 3\n");
for(i=1;i<=3;i++)
{
printf("%d",i);
for(j=1;j<=3;j++)
{
if(i>j) m=(i-1)*i/2+(i-j+1);
else m=(j-1)*j/2+(j-i+1);
printf(" %d",b[m]);
if(j==3)printf("\n");
}
}
return 0;
}
这个上三角区域的话,还可以继续压缩,把对角线那部分也去掉,(1,1)这种是不允许的,所以去掉也没关系,我就懒得再写了。
② 邻接表(逆邻接表)
==基本想法==,这个由两部分组成,一部分是边表,另外一部分是顶点表。
具体形式如下。 //参照王道的数据结构一书
顶点域 | 边表头指针 |
---|---|
data | firstarc |
邻接点域 | 指针域 |
---|---|
adjvex | nextarc |
顶点表结点由顶点域(data)和指向第一条邻接边的指针(firstarc)构成,边表(邻接表)结点由邻接点域(adjvex)和指向下一条的指针域(nextarc)构成。
下面是书上的图
这里给出简化过的代码(上面是用C写的,下面这个是用C++写的,把指针改成了引用)
//这个写法要简洁一些
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 21
typedef struct ArcNode//邻接表中的边表结点
{
int vertex;//顶点下标
struct ArcNode *next;//指向下一个邻接点
}ArcNode;
typedef struct//图结构
{
ArcNode* head[N];//邻接表边表头指针 ,这里只是定义指针数组 没有为指针数组的元素分配内存;
int vNum;//顶点数
int eNum;//边数
}Graph; //有向图
void CreateGraph(Graph& g)
{
int n,m,i;
scanf("%d%d",&g.vNum,&g.eNum); //n表示顶点数,m表示边数
m=g.eNum;
for(i=1;i<=g.vNum;i++)
{
g.head[i]=(ArcNode *)malloc(sizeof(ArcNode));
g.head[i]->next=NULL;
}
int start,end;
while(m-->0)
{
scanf("%d %d",&start,&end);
ArcNode *L=(ArcNode *)malloc(sizeof(ArcNode));
L->vertex=end;
L->next=NULL;
ArcNode *p=g.head[start];
while(p->next!=NULL)
{
if(end>p->next->vertex)
p=p->next;
else break;
}
if(p->next==NULL){
p->next=L;
}
else{
L->next=p->next;
p->next=L;
}
}
for(i=1;i<=g.vNum;i++)
{
g.head[i]=g.head[i]->next;
}
}
void PrintGraph(Graph& g)
{
int i;
for(i=1;i<=g.vNum;i++)
{
ArcNode *p = g.head[i];
printf("%d",i);
while(p)
{
printf("->%d",p->vertex);
p=p->next;
}
printf("\n");
}
}
int main()
{
Graph g;
CreateGraph(g);
PrintGraph(g);
return 0;
}
这里逆邻列表和邻接表实现方式大体上是一样,就不多介绍了。
要注意的是,邻接表(逆邻接表)可以表示有向图的出(入)度,也可以表示无向图。而对于无向图来说,在图稠密的时候,和邻接矩阵相比的话,并不占优势。在图稀疏的时候,邻接表的存储占优。
③ 十字链表
就是把邻接表和逆邻接表整合到了一起。
下面部分参考王道书上
#include<stdio.h>
#include<stdlib.h>
#define N 21
typedef struct ArcNode{
struct ArcNode *hlink,*tlink;
int headvex,tailvex;
}ArcNode;
typedef struct VerNode{
struct ArcNode *in,*out; //firstin,firstout 顶点结点
}VerNode;
typedef struct{
VerNode* head[N];
int vNum;
int eNUm;
}Graph;
void CreateGraph(Graph &g)
{
scanf("%d %d",&g.vNum,&g.eNUm);
int n=g.eNUm,i;
for(i=1;i<=g.vNum;i++)
{
g.head[i]=(VerNode *)malloc(sizeof(VerNode));
g.head[i]->in=NULL;
g.head[i]->out=NULL;
}
while(n-->0)
{
int start,end;
scanf("%d %d",&start,&end);
ArcNode *p=(ArcNode *)malloc(sizeof(ArcNode));
p->headvex=start;p->tailvex=end;
p->hlink=NULL;p->tlink=NULL;
if(g.head[start]->out==NULL)
{
g.head[start]->out=p;
}
else{
ArcNode *s=g.head[start]->out;
g.head[start]->out=p;
p->hlink=s;
}
if(g.head[end]->in==NULL)
{
g.head[end]->in=p;
}
else{
ArcNode *s=g.head[end]->in;
g.head[end]->in=p;
p->tlink=s;
}
}
}
void PrintGraph(Graph& g)
{
int i;
for(i=1;i<=g.vNum;i++)
{
ArcNode *p = g.head[i]->out;
printf("%d出:",i);
while(p)
{
printf("->%d",p->tailvex);
p=p->hlink;
}
printf("\n");
p=g.head[i]->in;
printf("%d进:",i);
while(p)
{
printf("<-%d",p->headvex);
p=p->tlink;
}
printf("\n");
}
}
int main()
{
Graph g;
CreateGraph(g);
PrintGraph(g);
return 0;
}
/*
4 7
1 2
1 3
3 1
3 4
4 1
4 2
4 3
*/
④多重邻接表
这个是对无向表有用,下面还是一样先看书
我认为比较重要的一点就是讲差别那块,开门见山。下面给出代码(C++)
//邻接多重表,适用于稀疏无向图。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 21
typedef struct ArcNode{
int ivex,jvex;
struct ArcNode *ilink,*jlink;
}ArcNode;
typedef struct Graph{
int vNum;
int eNum;
ArcNode* Vertex[N];
}Graph;
Graph *InitGraph()
{
int i;
Graph *G=(Graph *)malloc(sizeof(Graph));
printf("请输入图的顶点和边的个数:\n");
scanf("%d%d",&G->vNum,&G->eNum);
for(i=1;i<=G->eNum;i++)
G->Vertex[i]=NULL;
return G;
}
void Create(Graph *G)
{
int i,j,k,start,end;
ArcNode *p,*s;
for(i=0;i<G->eNum;i++)
{
scanf("%d%d",&start,&end);
p=(ArcNode *)malloc(sizeof(ArcNode));
p->ivex=start;p->jvex=end;
p->ilink=NULL;p->jlink=NULL;
s=G->Vertex[start];
if(s==NULL) G->Vertex[start]=p;
else{
if(s->ivex==start)
{ p->ilink=s->ilink; //不影响数据表示,因此在头部插入没事
s->ilink=p;
}
else{
p->ilink=s->jlink;
s->jlink=p;
}
}
s=G->Vertex[end];
if(s==NULL) G->Vertex[end]=p;
else{
if(s->ivex==end)
{
p->jlink=s->ilink;
s->ilink=p;
}
else{
p->jlink=s->jlink;
s->jlink=p;
}
}
}
}
void OutGraph(Graph *G)
{
int i;
for(i=1;i<=G->vNum;i++)
{
ArcNode *p=G->Vertex[i];
printf("%d",i);
while(p!=NULL)
{
if(p->ivex==i)
{
printf("->%d",p->jvex);
p=p->ilink;
}
else{
printf("->%d",p->ivex);
p=p->jlink;
}
}
printf("\n");
}
}
int main()
{
Graph *G=InitGraph();
Create(G);
OutGraph(G);
return 0;
}
/*
5 6
1 2
1 4
3 2
3 4
5 2
3 5
*/
感觉每次写之前都感觉有好多想写的,结果写着写着就变成了。。。
下次我把图的具体操作什么的都补上,可以的话我还想用电容笔,录个视频讲下。看看我有没有这个实力了。