C语言-图的表示与实现

文章详细介绍了图的邻接矩阵和邻接表两种表示法,包括它们的定义、性质、创建操作以及相关特点,特别强调了在不同图类型中的应用和空间复杂度。
摘要由CSDN通过智能技术生成

一、图的邻接矩阵表示法

1.定义

        在图的邻接矩阵表示法中,用一个一维数组来存储图中每个顶点的信息,用一个二维数组来存储图中边或弧的信息,这个二维数组称为邻接矩阵。

        对于图,邻接矩阵arcs定义如下:

存储类型定义如下:

typedef struct{
    int vexs[MAX_VERTEX_NUM];
    int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
    int vexnum,arcnum;
    int kind;
}

        其中,VertexType是顶点的类型,ArcTypc是边的类型。kind分量用来标志图的类别,本文约定取0,1,2,3四个值,分别用来作为有向图、有向网、无向图和无向网的标志

对于无向图,其邻接矩阵有以下性质:

(1)矩阵沿主对角线对称;

(2)矩阵中非0元的个数等于边数的2倍;

(3)第i行(或第i列)非0元是顶点i的邻接点;

(4)顶点i的度等于第i行(或第i列)非0元的个数。

 对于有向图,其邻接矩阵有以下性质:

(1)矩阵一般非对称;

(2)矩阵中非0元的个数等于边数;

(3)第i行非0元是顶点i的出边邻接点,第i列非0元是顶点i的入边邻接点;

(4)顶点i的出度于第i行非0元的个数,入度等于第i列非0元的个数。

对于网,邻接矩阵可以按如下定义:

一个有向网和它的邻接矩阵存储示意图

        邻接矩阵空间开销只与图的顶点数n有关、与边数无关,空间复杂度是O(n^{2})。所以,从空间利用率的角度上讲,邻接矩阵适合存储稠密图。当用来存储稀疏图时,邻接矩阵就是一个稀疏矩阵。

2.相关操作

(1)创建图

int locatevex(graph* g, int v) {
	for (int i = 0; i < g->vexnum; i++) {
		if (g->vexs[i] == v) return i;
	}
	return -1;
}

void creategraph(graph* g) {
	int i, j, vi, vj, k;
	scanf_s("%d %d %d", &g->vexnum, &g->arcnum, &g->kind);//输入顶点数,边数和图类别
	for (i = 0; i < g->vexnum; i++) {
		scanf_s("%d", &g->vexs[i]);
	}
	for (i = 0; i < g->vexnum; i++) {//初始化邻接矩阵
		for (j = 0; j < g->vexnum; j++) {
			g->arcs[i][j] = 0;
		}
	}
	for (k = 0; k < g->arcnum; k++) {
		scanf_s("%d", &vi, &vj);
		i = locatevex(g, vi);//寻找顶点的存储下标
		j = locatevex(g, vj);
		g->arcs[i][j] = 1;//置弧
		if (g->kind == 2) g->arcs[j][i] = 1;//无向图置对称弧
	}
}

(2)插入边


void insertarc(graph* g, int vi, int vj) {
	int i= locatevex(g, vi);//寻找顶点的存储下标
	int j = locatevex(g, vj);
	if (i != -1 && j != -1) {
		g->arcnum++;
		g->arcs[i][j] = 1;
		if (g->kind == 2) g->arcs[j][i] = 1;
	}
}

(3)寻找邻接点

操作关键:寻找第i行(或第i列)的非0元

void firstadjvex(graph* g, int v) {//寻找下标为v的顶点的第一个邻接点
	for (int j = 0; j < g->vexnum; j++) {
		if (g->arcs[v][j] != 0) return j;
	}
	return -1;
}

二、图的邻接表表示法

1.定义

        邻接表表示法是图的一种链式存储结构。对于图中每个顶点v,把与v有边相连的所有顶点用一条称为边链表的单链表连接起来,边链表中的结点叫做边结点。边结点的结构为:

        其中, adjvex是邻接点域,存放v邻接点的位置信息; weight是域存放边的权值;nextarc是指针域,存放链表的后继指针。

n个顶点的图,具有n条这样的边链表,在每条边链表上附设一个表头结点,表头结点的结构如下: 

        其中,data是数据域,存放顶点v的数据信息; firstarc是指针域,存放边链表的头指针。将n个这样的表头结点存放在一个数组中,以便随机访问任意一个顶点的边链表。

存储结构如下:

typedef struct ArcNode {//边节点
	int adjvex;//邻接点的位置下标
	int weight;//边的权值
	struct ArcNode* nextarc;//边链的后继指针
}arcnode;

typedef struct vertexnode {//表头节点
	int data;//顶点数据
	arcnode* firstarc;//边链头指针
}vertexnode;

typedef struct {
	vertexnode vertices[MAX_VERTEX_NUM];//表头节点数组
	int vexnum, arcnum;//顶点数量、边数
	int kind;//图的类别标志
}graph;

对于无向图G5, 

具有n个顶点和e条边的无向图G的邻接表有如下特点:

(1)每条边有两个边结点,边结点的总数等于2e;

(2)遍历顶点v的边链表能访问到v的所有邻接点;

(3)顶点v的度TD(v)等于v的边链表的长度。

对于有向图G6,

 

具有n个顶点和e条边的有向图G的邻接表有如下特点:

(1)顶点vi的边链表是一条出边链,链表中每个结点都是v的出边邻接点;

(2)顶点v的出度OD(v)等于v边链表的长度;

(3)寻访顶点v的入边邻接点或计算v的入度ID(v)需要周游整个邻接表; 

(4)每条弧用1个边结点表示,邻接表中边结点的总数等于边数e。

        在有向图的邻接表中找任一顶点的入边邻接点或求其入度不够方便。为此,可以建立有向图的逆邻接表,即将顶点的人边邻接点链接成边链表。

        邻接表存储结构的空间开销取决与图的顶点数n和边数e,空间复杂度为O(n+e)。故其适用于表示一个稀疏图。

2.相关操作

(1) 创建图

int locatevex(graph* g, int v) {//查找值为v的节点在图g中的存储位置
	for (int i = 0; i < g->vexnum; i++) {
		if (g->vertices[i].data == v) return i;
	}
	return -1;
}

void creategraph(graph* g) {
	int i, vi, vj, j;
	scanf_s("%d %d %d", &g->vexnum, &g->arcnum, &g->kind);
	for (i = 0; i < g->vexnum; i++) {
		scanf_s("%d", g->vertices[i].data);//输入顶点v的值
		g->vertices[i].firstarc = NULL;
	}
	for (k = 0; k < g->arcnum; k++) {
		scanf_s("%d %d", &vi, &vj);
		i = locatevex(g, vi);
		j = locatevex(g, vj);
		arcnode* p = (arcnode*)malloc(sizeof(arcnode));
		p->adjvex = j;
		p->nextarc = g->vertices[i].firstarc;//这里是头插法
		g->vertices[i].firstarc = p;
		if (g->kind == 2) {//g是无向图,插入对称弧
			arcnode* p = (arcnode*)malloc(sizeof(arcnode));
			p->adjvex = i;
			p->nextarc = g->vertices[j].firstarc;
			g->vertices[j].firstarc = p;
		}
	}
}

(2)插入边

void insertarc(graph* g, int vi, int vj) {
	int i = locatevex(g, vi);
	int j = locatevex(g, vj);
	if (i != -1 && j != -1) {
		arcnode* p = (arcnode*)malloc(sizeof(arcnode));
		p->adjvex = j;
		p->nextarc = g->vertices[i].firstarc;
		g->vertices[i].firstarc = p;
		if (g->kind == 2) {
			arcnode* p = (arcnode*)malloc(sizeof(arcnode));
			p->adjvex = i;
			p->nextarc = g->vertices[j].firstarc;
			g->vertices[j].firstarc = p;
		}
	}
}

(3)寻找邻接点

int NextAdjVex(graph* g, int v, int w) {
	//求存储在下标为v的顶点,值为w的节点的下一个邻接点
	//即寻找值为w的边节点的直接后续
	arcnode* p = g->vertices[v].firstarc;
	while (p && p->adjvex != w) p = p->nextarc;
	if (!p || !p->nextarc) {
		return -1;
	}
	else return p->nextarc->adjvex;
}

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值