数据结构20————图的存储结构
一. 目录
文章目录
二. 邻接矩阵
1. 存储形式
- 使用一维数组存储顶点信息,使用二维数组存储边信息。
- 如果是图a[i][j]=1表示顶点i和顶点j直接存在边,a[i][j]=0表示顶点i和j不存在边。
- 如果是网,则a[i][j=m(m为数字)表示顶点i和j之间存在一个权值为m的边,a[i][j]=无穷表示顶点i和j之间不存在边。
- 无向图的邻接矩阵为对称矩阵
优点:简单易用
缺点:对于稀疏矩阵而言,太过浪费空间
2. 图例
无向图:
有向图
网
3. 数据结构的结构体设计
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 30
typedef char Vertype;
typedef struct{
int acre[MAXVEX][MAXVEX];//邻接表
Vertype Vex[MAXVEX];//顶点信息
int vexnum;//顶点数量
int acrenum;//边的数量
}AdjMatrix;
4. 实现有向图的存储,并统计出度和入度
AdjMatrix *Create(){
AdjMatrix *adj;
adj = (AdjMatrix*)malloc(sizeof(AdjMatrix));
printf("请输入图的顶点数和边数:");
scanf("%d%d",&adj->vexnum,&adj->acrenum);
int i;
printf("请输入点信息:\n");
for(i = 0;i < adj->vexnum;i++){
getchar();
printf("请输入第%d个顶点:",i+1);
scanf("%c",&adj->Vex[i]);
}
int j,k;
char a[3];
for(i=0;i<adj->vexnum;i++) // 初始化
for(j=0;j<adj->vexnum;j++)
adj->acre[i][j]=0;
getchar();
printf("\n请输入边信息:如ab\n");
for(i=0;i<adj->acrenum;i++){
printf("请输入第%d个边:",i+1);
gets(a);
j=0;
k=0;
while(adj->Vex[j++] != a[0]&&j<adj->acrenum);
while(adj->Vex[k++] != a[1]&&k<adj->acrenum);
adj->acre[j-1][k-1]=1;
}
printf("\n你输入的邻接矩阵如下:\n");
for(i=0;i<adj->vexnum;i++){
for(j=0;j<adj->vexnum;j++){
printf("%4d",adj->acre[i][j]);
}
printf("\n");
}
return adj;
}
void Statistics(AdjMatrix *adj){
int i,j;
int countA,countB;
for(i=0;i<adj->vexnum;i++){
countA=0;
countB=0;
for(j=0;j<adj->acrenum;j++){
if(adj->acre[i][j]==1){//出度
countA++;
}
if(adj->acre[j][i]==1){//入度
countB++;
}
}
printf("顶点%c,出度为%d,入度为%d\n",adj->Vex[i],countA,countB);
}
}
三. 邻接表
1. 存储形式
- 图的顶点用一个以为数组存储,该顶点的所有邻接点构成一个线性表,组成单链表,连接在该顶点后。
- 在无向图中,每个边会出现两次
- 对于有向图,其后面可以跟它的邻接点(邻接表),也可以跟它的逆邻接点(逆邻接表).
优点:相对于邻接表而言,解决了稀疏矩阵太过浪费空间的问题
缺点:对于有向图而言,邻接表统计每个顶点的入度都要遍历整个图,,而逆邻接表统计每个顶点的出度都要遍历整个图,对于有向图而言,对于无向图而言,每个边出现两次,浪费了一半的空间,并且删除操作需要删除两次。
2. 图例
无向图
有向图
网
3. 数据结构的结构体设计
typedef char Vertype;
typedef struct ArcNode{
int adhVex;//弧尾
struct ArcNode *next;
}ArcNode;
typedef struct VertexNode{
Vertype vexdata; //点
ArcNode *head;
}VertexNode;
typedef struct{
VertexNode vertex[MAXVEX];
int vexnum; // 顶点数
int arcnum; //边数
}AdjList;
4. 实现有向图的存储,并统计出度和入
AdjList *Create(){
AdjList *adj;
adj = (AdjList *)malloc(sizeof(AdjList));
printf("请输入有向图的顶点的数量和边的数量:");
scanf("%d%d",&adj->vexnum,&adj->arcnum);
int i;
printf("请输入点信息:\n");
for(i=0;i < adj->vexnum;i++){
getchar();
printf("请输入第%d个顶点:",i+1);
scanf("%c",&adj->vertex[i].vexdata);
adj->vertex[i].head = NULL;
}
getchar();
int j,k;
char a[3];
ArcNode *arc;
ArcNode *p;
printf("\n请输入边信息如ab:\n");
for(i=0;i<adj->arcnum;i++){
printf("请输入第%d个边:",i+1);
gets(a);
for(j=0;adj->vertex[j].vexdata != a[0] &&j<adj->vexnum;j++);
for(k=0;adj->vertex[k].vexdata != a[1] &&k<adj->vexnum;k++);
arc = (ArcNode *)malloc(sizeof(ArcNode));
arc->adhVex = k;
arc->next = adj->vertex[j].head;
adj->vertex[j].head = arc;
}
printf("你输入的邻接表如下:\n");
for(i=0;i<adj->vexnum;i++){
printf("顶点:%c ",adj->vertex[i].vexdata);
p = adj->vertex[i].head;
while(p!=NULL){
printf("-->%d",p->adhVex);
p=p->next;
}
printf("\n");
}
return adj;
}
void Statistics(AdjList *adj){
int i,j;
int countA,countB;
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
countA=0;
countB=0;
printf("顶点%c:",adj->vertex[i].vexdata);
p=adj->vertex[i].head;
while(p!=NULL){
countA++;
p=p->next;
}
printf("出度:%d,",countA);
for(j=0;j<adj->vexnum;j++){
p=adj->vertex[j].head;
while(p!=NULL){
if(p->adhVex==i){
countB++;
}
p=p->next;
}
}
printf("入度:%d\n",countB);
}
}
四. 十字链表
1. 存储形式
- 对于有向图而言,每个顶点相当于连接两个链表,一个连所有以它为入度的邻接点,一条连接所有以它为出度的邻接点。对于每个边而言,两个指针域,一个连接与他相同起点的下一个边,一个连接与他终点相同的下一个边。
优点:解决了有向图邻接表入度的问题。
缺点:结构有些复杂.
2.图例
3.数据结构的结构体设计
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 30
typedef char Vertype;
//弧尾-->弧头 <尾,头>
typedef struct ArcNode{
int tailvex;//弧尾
int headvex;//弧头
struct ArcNode *tailLink;//和它具有相同弧尾的边
struct ArcNode *headLink;//和它具有相同弧头的边
}ArcNode;
typedef struct VertexNode{
Vertype vexdata; //点
ArcNode *firstout;//连接以它为弧尾的边
ArcNode *firstin;//连接以它为弧头的边
}VertexNode;
typedef struct{
VertexNode vertex[MAXVEX];
int vexnum; // 顶点数
int arcnum; //边数
}AdjList;
4.实现有向图的存储,并统计出度和入
AdjList *Create(){
AdjList *adj;
adj = (AdjList *)malloc(sizeof(AdjList));
printf("请输入有向图的顶点的数量和边的数量:");
scanf("%d%d",&adj->vexnum,&adj->arcnum);
int i=0;
printf("请输入点信息:\n");
for(i=0;i<adj->vexnum;i++){
getchar();
printf("请输入第%d个顶点:",i+1);
scanf("%c",&adj->vertex[i].vexdata);
adj->vertex[i].firstout=NULL;
adj->vertex[i].firstin=NULL;
}
getchar();
int j,k;
ArcNode *arc;
char a[3];
printf("\n请输入边信息如ab:\n");
for(i=0;i<adj->arcnum;i++){
printf("请输入第%d条边:",i+1);
gets(a);
for(j=0;adj->vertex[j].vexdata!=a[0]&&j<adj->vexnum;j++);
for(k=0;adj->vertex[k].vexdata!=a[1]&&k<adj->vexnum;k++);
arc=(ArcNode *)malloc(sizeof(ArcNode));
arc->tailvex=j;
arc->headvex=k;
arc->tailLink=adj->vertex[j].firstout;
adj->vertex[j].firstout=arc;
arc->headLink=adj->vertex[k].firstin;
adj->vertex[k].firstin=arc;
}
printf("你输入的十字链表如下:\n");
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
printf("顶点:%c",adj->vertex[i].vexdata);
p = adj->vertex[i].firstout;
while(p!=NULL){
printf("-->%d",p->headvex);
p=p->tailLink;
}
printf("\n");
printf("顶点:%c",adj->vertex[i].vexdata);
p = adj->vertex[i].firstin;
while(p!=NULL){
printf("<==%d",p->tailvex);
p=p->headLink;
}
printf("\n\n");
}
return adj;
}
Statistics(AdjList *adj){
int i;
int countA,countB;
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
for(p=adj->vertex[i].firstout,countA=0;p!=NULL;p=p->tailLink,countA++);
for(p=adj->vertex[i].firstin,countB=0;p!=NULL;p=p->headLink,countB++);
printf("点%c的出度为:%d,入度为:%d\n",adj->vertex[i].vexdata,countA,countB);
}
}
五.邻接多重表
1.存储形式
- 无向图的每个边只会出现一次。每个边两个指针域,一个指向顶点i的下一个边,一个指向顶点j的下一个边.
优点:解决了无向图邻接表中,一条边出现两次的问题。
缺点:结构有些复杂.
2. 图例
3. 数据结构的结构体设计
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 30
typedef char Vertype;
typedef struct ArcNode{
int ivex;//边的i顶点
int jvex;//边的j顶点
struct ArcNode *ilink;//指向同样具有i顶点的边
struct ArcNode *jlink;//指向同样具有j顶点的边
}ArcNode;
typedef struct VertexNode{
Vertype vexdata; //点
ArcNode *firstdge;//连接以它为弧尾的边
}VertexNode;
typedef struct{
VertexNode vertex[MAXVEX];
int vexnum; // 顶点数
int arcnum; //边数
}AdjList;
4. 实现无向图的存储,并统计出度和入
AdjList *Create(){
AdjList *adj;
adj = (AdjList *)malloc(sizeof(AdjList));
printf("请输入有向图的顶点的数量和边的数量:");
scanf("%d%d",&adj->vexnum,&adj->arcnum);
int i=0;
printf("请输入点信息:\n");
for(i=0;i<adj->vexnum;i++){
getchar();
printf("请输入第%d个顶点:",i+1);
scanf("%c",&adj->vertex[i].vexdata);
adj->vertex[i].firstdge=NULL;
}
getchar();
int j,k;
ArcNode *arc;
char a[3];
printf("\n请输入边信息如ab:\n");
for(i=0;i<adj->arcnum;i++){
printf("请输入第%d条边:",i+1);
gets(a);
for(j=0;adj->vertex[j].vexdata!=a[0]&&j<adj->vexnum;j++);
for(k=0;adj->vertex[k].vexdata!=a[1]&&k<adj->vexnum;k++);
arc=(ArcNode *)malloc(sizeof(ArcNode));
arc->ivex=j;
arc->jvex=k;
arc->ilink=adj->vertex[j].firstdge;
adj->vertex[j].firstdge=arc;
arc->jlink=adj->vertex[k].firstdge;
adj->vertex[k].firstdge=arc;
}
printf("你输入的邻接多重表如下:\n");
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
printf("顶点:%c",adj->vertex[i].vexdata);
p = adj->vertex[i].firstdge;
while(p!=NULL){
if(p->ivex==i){
printf("-->%d",p->jvex);
p=p->ilink;
}else{
printf("-->%d",p->ivex);
p=p->jlink;
}
}
printf("\n");
}
return adj;
}
void Statistics(AdjList *adj){
int i;
int count;
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
for(p=adj->vertex[i].firstdge,count=0;p!=NULL;count++){
if(p->ivex==i){
p=p->ilink;
}else{
p=p->jlink;
}
}
printf("点%c的度为:%d\n",adj->vertex[i].vexdata,count);
}
}
六. 边集数组
1. 存储形式
- 由两个一维数组组成
- 一个一维数组存储点
- 一个一维数组存储边(起点,终点,权值).
2. 图例
3. 数据结构的结构体设计
#include<stdlib.h>
#define MAXVEX 30
typedef char Vertype;
typedef struct ArcNode{
int ivex;//边的i顶点
int jvex;//边的j顶点
struct ArcNode *ilink;//指向同样具有i顶点的边
struct ArcNode *jlink;//指向同样具有j顶点的边
}ArcNode;
typedef struct VertexNode{
Vertype vexdata; //点
ArcNode *firstdge;//连接以它为弧尾的边
}VertexNode;
typedef struct{
VertexNode vertex[MAXVEX];
int vexnum; // 顶点数
int arcnum; //边数
}AdjList;
4. 实现有向图的存储,并统计出度和入
AdjList *Create(){
AdjList *adj;
adj = (AdjList *)malloc(sizeof(AdjList));
printf("请输入有向图的顶点的数量和边的数量:");
scanf("%d%d",&adj->vexnum,&adj->arcnum);
int i=0;
printf("请输入点信息:\n");
for(i=0;i<adj->vexnum;i++){
getchar();
printf("请输入第%d个顶点:",i+1);
scanf("%c",&adj->vertex[i].vexdata);
adj->vertex[i].firstdge=NULL;
}
getchar();
int j,k;
ArcNode *arc;
char a[3];
printf("\n请输入边信息如ab:\n");
for(i=0;i<adj->arcnum;i++){
printf("请输入第%d条边:",i+1);
gets(a);
for(j=0;adj->vertex[j].vexdata!=a[0]&&j<adj->vexnum;j++);
for(k=0;adj->vertex[k].vexdata!=a[1]&&k<adj->vexnum;k++);
arc=(ArcNode *)malloc(sizeof(ArcNode));
arc->ivex=j;
arc->jvex=k;
arc->ilink=adj->vertex[j].firstdge;
adj->vertex[j].firstdge=arc;
arc->jlink=adj->vertex[k].firstdge;
adj->vertex[k].firstdge=arc;
}
printf("你输入的邻接多重表如下:\n");
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
printf("顶点:%c",adj->vertex[i].vexdata);
p = adj->vertex[i].firstdge;
while(p!=NULL){
if(p->ivex==i){
printf("-->%d",p->jvex);
p=p->ilink;
}else{
printf("-->%d",p->ivex);
p=p->jlink;
}
}
printf("\n");
}
return adj;
}
void Statistics(AdjList *adj){
int i;
int count;
ArcNode *p;
for(i=0;i<adj->vexnum;i++){
for(p=adj->vertex[i].firstdge,count=0;p!=NULL;count++){
if(p->ivex==i){
p=p->ilink;
}else{
p=p->jlink;
}
}
printf("点%c的度为:%d\n",adj->vertex[i].vexdata,count);
}
}
七. 源码地址
八. 参考资料
《大话数据结构》
《数据结构与算法》