图的4种存储(简化版)

最近没学什么东西,但图基本的一些存储方式,马马虎虎懂了。

图的存储方式

  1. 邻接矩阵
  2. 邻接表(逆邻接表)
  3. 十字链表
  4. 多重邻接表

为了突出重点,在下面给的例子中,有些的图会默认为无权图。

① 邻接矩阵

     ==基本想法==,就是用一个(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)这种是不允许的,所以去掉也没关系,我就懒得再写了。

② 邻接表(逆邻接表)

==基本想法==,这个由两部分组成,一部分是边表,另外一部分是顶点表。
具体形式如下。 //参照王道的数据结构一书
顶点域边表头指针
datafirstarc
邻接点域指针域
adjvexnextarc

顶点表结点由顶点域(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 
*/ 

感觉每次写之前都感觉有好多想写的,结果写着写着就变成了。。。

下次我把图的具体操作什么的都补上,可以的话我还想用电容笔,录个视频讲下。看看我有没有这个实力了。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值