数据结构—邻接表法代码实现

邻接表法

  • 图的邻接表(adjacency list)是一种顺序与链式存储相结合的存储方法。
  • 对于含有n个顶点的图,每个顶点建立一个单链表,第i(0≤n-1)个单链表中的结点表示关联于顶点i的边(对有向图是以顶点i为起点的边),也就是将顶点i的所有邻接点(对有向图是出边邻接点)链接起来,其中每个结点表示一条边的信息。
  • 每个单链表再附设一个头结点,并将所有头结点构成一个头结点数组adjlist[ ],adjlist[i]表示顶点i的单链表的头结点,这样就可以通过顶点i快速地找到对应的单链表。
  • 在邻接表中有两种类型的结点,一种是头结点,其个数恰好为图中顶点的个数;另一种是边结点,也就是单链表中的结点。对于无向图,这类结点的个数等于边数的两倍;对于有向图,这类结点的个数等于边数。
    在这里插入图片描述
  • 其中,边结点由3个域组成,adjvex表示与顶点i邻接的顶点编号,nextarc指向下一个边结点,weight存储与该边相关的信息,如权值等。头结点由两个域组成,data存储顶点的名称或其他信息,firstarc指向顶点i的单链表中的首结点。

邻接表存储类型的声明

typedef struct ArcNode
{
	int adjvex; //该边的邻接点编号
	struct ArcNode* nextarc; //指向下条边的指针
	int weight; //该边的相关信息,如权值
}ArcNode; //边结点的类型

typedef struct VNode
{
	VertexType data; //顶点信息
	ArcNode* firstarc; //指向第一个边结点
}VNode;

typedef struct
{
	VNode adjlist[MaxVertexNum]; //邻接表的头结点数组
	int n, e; //图中的顶点数n和边数e
}AdjGraph; //完整的图邻接表类型

不带权无向图基本操作的代码实现

#include<stdio.h>
#include<malloc.h>
#define MaxVertexNum 100
typedef char VertexType;
typedef struct ArcNode
{
	int adjvex; //该边的邻接点编号
	struct ArcNode* nextarc; //指向下条边的指针
	//int weight; //该边的相关信息,如权值
}ArcNode; //边结点的类型

typedef struct VNode
{
	VertexType data; //顶点信息
	ArcNode* firstarc; //指向第一个边结点
}VNode;

typedef struct
{
	VNode adjlist[MaxVertexNum]; //邻接表的头结点数组
	int vexnum, edgenum; //图中的顶点数和边数
}AdjGraph; //完整的图邻接表类型

void InitG(AdjGraph& g)
{
	int i;
	for (i = 0; i < MaxVertexNum; i++)
	{
		g.adjlist[i].data = '\0';
		g.adjlist[i].firstarc = NULL;
	}
	g.vexnum = 0;
	g.edgenum = 0;
}

void CreateVNde(AdjGraph& g)
{
	int i = 0, count = 0;
	printf("输入图的顶点:");
	VertexType ch = getchar();
	while (ch != '#') //#表示结束输入
	{
		g.adjlist[i].data = ch;
		g.adjlist[i].firstarc = NULL;
		i++;
		count++; //统计顶点个数
		ch = getchar();
	}
	g.vexnum = count;
}

void CreateANode(AdjGraph& g, VertexType ch, int num)
{
	ArcNode* p, * r = g.adjlist[0].firstarc;
	int i, j, k;
	while (num--)
	{
		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->nextarc = NULL;
		printf("输入顶点的编号:");
		scanf("%d", &i);
		for (j = 0; j < g.vexnum; j++)
			if (g.adjlist[j].data == ch) k = j;
		if (i != k)
		{
			p->adjvex = i;
			if (g.adjlist[k].firstarc == NULL)
			{
				g.adjlist[k].firstarc = p;
				r = p;
			}
			else
			{
				r->nextarc = p;
				r = p;
			}
		}
	}
}

bool Adjacent(AdjGraph g, VertexType x, VertexType y) //判断图G是否存在弧<x,y>
{
	int i, j, k;
	for (i = 0; i < g.vexnum; i++)
	{
		if (g.adjlist[i].data == x) j = i;
		if (g.adjlist[i].data == y) k = i;
	}
	ArcNode* p = g.adjlist[j].firstarc;
	while (p != NULL)
	{
		if (p->adjvex == k) return true;
		else p = p->nextarc;
	}
	return false;
}

void Neighbors(AdjGraph g, VertexType x) //列出图G中与结点x邻接的弧
{
	int i, j;
	for (i = 0; i < g.vexnum; i++)
		if (g.adjlist[i].data == x) j = i;
	ArcNode* p = g.adjlist[j].firstarc;
	while (p != NULL)
	{
		printf("%c ", g.adjlist[p->adjvex].data);
		p = p->nextarc;
	}
}

bool InsertVertex(AdjGraph& g, VertexType x) //在图G中插入顶点x
{
	int i;
	for (i = 0; i < g.vexnum; i++)
		if (g.adjlist[i].data == x) return false; //与图G中原有顶点值相同
	g.adjlist[g.vexnum].data = x;
	g.adjlist[g.vexnum].firstarc = NULL;
	g.vexnum++;
	return true;
}

bool DeleteVertex(AdjGraph& g, VertexType x) //在图G中删除顶点x。
{
	int i, j;
	for (i = 0; i < g.vexnum; i++)
		if (g.adjlist[i].data == x) j = i;
	if (j >= g.vexnum) return false;

	//删除顶点x所在邻接表中的一行数据
	ArcNode* p = g.adjlist[j].firstarc, * q = p->nextarc;
	while (q != NULL)
	{
		free(p);
		p = q;
		q = q->nextarc;
	}
	g.adjlist[j].firstarc = NULL;


	//删除其他顶点与顶点x有关的数据
	for (i = 0; i < g.vexnum; i++)
	{
		p = g.adjlist[i].firstarc;
		while (p != NULL)
		{
			if (p->adjvex == j)
			{
				if (p->adjvex == g.adjlist[i].firstarc->adjvex)
				{
					g.adjlist[i].firstarc = p->nextarc;
					free(p);
					break;
				}
				else
				{
					q->nextarc = p->nextarc;
					free(p);
					break;
				}
			}
			q = p;
			p = p->nextarc;
		}
	}

	//调整顶点表,将其他元素上移填补删除元素的空缺
	for (i = j; i < g.vexnum; i++)
	{
		g.adjlist[i].data = g.adjlist[i + 1].data; //对应顶点上移
		g.adjlist[i].firstarc = g.adjlist[i + 1].firstarc; //对应边结点链表上移
	}

	//修改边结点边表中的数据
	for (i = 0; i < g.vexnum; i++)
	{
		p = g.adjlist[i].firstarc;
		while (p != NULL)
		{
			if (p->adjvex > j) p->adjvex = p->adjvex - 1;
			p = p->nextarc;
		}
	}
	g.vexnum--; //顶点数-1
	return true;
}

bool AddEdge(AdjGraph& g, VertexType x, VertexType y) //若无向边(x, y)不存在,则向图G中添加该边
{
	int i, j, k;
	for (i = 0; i < g.vexnum; i++)
	{
		if (g.adjlist[i].data == x) j = i;
		if (g.adjlist[i].data == y) k = i;
	}
	if (j >= g.vexnum || k >= g.vexnum) return false; //不存在顶点x或顶点y
	else
	{
		ArcNode* p = g.adjlist[j].firstarc, * r = g.adjlist[k].firstarc, * q, * s;
		q = (ArcNode*)malloc(sizeof(ArcNode));
		q->adjvex = k;
		q->nextarc = p;
		g.adjlist[j].firstarc = q;

		s = (ArcNode*)malloc(sizeof(ArcNode));
		s->adjvex = j;
		s->nextarc = r;
		g.adjlist[k].firstarc = s;
		return true;
	}
}

bool RemoveEdge(AdjGraph& g, VertexType x, VertexType y) //若无向边(x, y)存在,则从图G中删除该边
{
	int i, j, k;
	for (i = 0; i < g.vexnum; i++)
	{
		if (g.adjlist[i].data == x) j = i;
		if (g.adjlist[i].data == y) k = i;
	}
	if (j >= g.vexnum || k >= g.vexnum) return false; //不存在顶点x或顶点y
	else
	{
		ArcNode* p = g.adjlist[j].firstarc, * q, * r = g.adjlist[k].firstarc, * s;
		while (p != NULL)
		{
			if (p->adjvex == k)
			{
				if (p->adjvex == g.adjlist[j].firstarc->adjvex)
				{
					g.adjlist[j].firstarc = p->nextarc;
					free(p);
					break;
				}
				else
				{
					q->nextarc = p->nextarc;
					free(p);
					break;
				}
			}
			else
			{
				q = p;
				p = p->nextarc;
			}
		}
		while (r != NULL)
		{
			if (r->adjvex == j)
			{
				if (r->adjvex == g.adjlist[k].firstarc->adjvex)
				{
					g.adjlist[k].firstarc = r->nextarc;
					free(r);
					break;
				}
				else
				{
					s->nextarc = r->nextarc;
					free(r);
					break;
				}
			}
			else
			{
				s = r;
				r = r->nextarc;
			}
		}
	}
	return true;
}

int FirstNeighbor(AdjGraph& g, VertexType x)
//求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1
{
	int i, j;
	for (i = 0; i < g.vexnum; i++)
		if (g.adjlist[i].data == x) j = i;
	if (j >= g.vexnum) return -1; //图中不存在x
	ArcNode* p = g.adjlist[j].firstarc;
	if (p == NULL) return -1; //若x没有邻接点
	else return p->adjvex; //返回顶点号
}

int NextNeighbor(AdjGraph& g, VertexType x, VertexType y)
//求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1
{
	int i, j, k;
	for (i = 0; i < g.vexnum; i++)
	{
		if (g.adjlist[i].data == x) j = i;
		if (g.adjlist[i].data == y) k = i;
	}
	if (j >= g.vexnum || k >= g.vexnum) return -1; //不存在顶点x或顶点y
	else
	{
		ArcNode* p = g.adjlist[j].firstarc;
		while (p != NULL)
		{
			if (p->adjvex == k)
			{
				if (p->nextarc != NULL) return p->nextarc->adjvex;
				else return -1;
			}
			else p = p->nextarc;
		}
	}
}

void CountDegree(AdjGraph& g) //统计顶点x的度
{
	ArcNode* p;
	int sumdegree = 0;
	printf("输出各顶点的度:\n");
	for (int i = 0; i < g.vexnum; i++)
	{
		p = g.adjlist[i].firstarc;
		int degree = 0;
		while (p != NULL)
		{
			degree++;
			p = p->nextarc;
		}
		sumdegree += degree;
		printf("顶点%c的度为:%d\n", g.adjlist[i].data, degree);
	}
	printf("无向图G中所有顶点的度之和为:%d", sumdegree);
	g.edgenum = sumdegree / 2;
}

void PrintG(AdjGraph g)
{
	int i;
	ArcNode* p;
	printf("输出各顶点的连接情况:\n");
	for (i = 0; i < g.vexnum; i++)
	{
		p = g.adjlist[i].firstarc;
		printf("%d(%c) ", i, g.adjlist[i].data);
		while (p != NULL)
		{
			printf("-%d(%c) ", p->adjvex, g.adjlist[p->adjvex].data);
			p = p->nextarc;
		}
		printf("\n");
	}
	printf("\n");
}

int main()
{
	AdjGraph g;
	VertexType ch;
	int i, num;
	ArcNode* p;

	InitG(g);
	CreateVNde(g);
	printf("\n");
	for (i = 0; i < g.vexnum; i++)
	{
		printf("创建顶点%c的边结点\n", g.adjlist[i].data);
		printf("输入要创建的边结点的个数:");
		scanf("%d", &num);
		CreateANode(g, g.adjlist[i].data, num);
		printf("\n");
	}

	PrintG(g);

	CountDegree(g);
	printf("\n");
	printf("\n无向图G的顶点数为:%d,边数为:%d\n", g.vexnum, g.edgenum);

	VertexType x, y;
	/*printf("\n-----判断图G是否存在弧<x,y>-----\n");
	x = 'C', y = 'F';
	printf("顶点%c和顶点%c之间存在弧吗?(1表示存在,0表示不存在)— %d", x, y, Adjacent(g, x, y));
	x = 'A', y = 'D';
	printf("\n顶点%c和顶点%c之间存在弧吗?(1表示存在,0表示不存在)— %d\n", x, y, Adjacent(g, x, y));*/

	/*printf("\n-----列出图G中与结点x邻接的弧-----\n");
	x = 'B';
	printf("与顶点%c连接的弧有:", x);
	Neighbors(g, x);
	printf("\n");*/

	/*printf("\n-----在图G中插入顶点x-----\n");
	x = 'G';
	printf("插入顶点%c", x);
	printf("插入成功了吗?— %d(1表示成功,0表示失败)\n", InsertVertex(g, x));
	printf("\n");
	PrintG(g);
	CountDegree(g);
	printf("\n");
	printf("\n无向图G的顶点数为:%d,边数为:%d\n", g.vexnum, g.edgenum);*/

	/*printf("\n-----在图G中插入删除顶点x-----\n");
	x = 'C';
	printf("删除顶点%c\n", x);
	printf("删除成功了吗?— %d(1表示成功,0表示失败)\n", DeleteVertex(g, x));
	printf("\n");
	PrintG(g);
	CountDegree(g);
	printf("\n");
	printf("\n无向图G的顶点数为:%d,边数为:%d\n", g.vexnum, g.edgenum);*/

	/*printf("\n-----图G中添加无向边(x, y)-----\n");
	x = 'E', y = 'F';
	printf("在顶点%c与顶点%c之间添加一条无向边\n", x, y);
	printf("添加成功了吗?— %d(1代表成功,原图中没有边(x, y),0代表失败,原图中已存在该边或者顶点x/顶点y不存在)\n", AddEdge(g, x, y));
	printf("\n");
	PrintG(g);
	CountDegree(g);
	printf("\n");
	printf("\n无向图G的顶点数为:%d,边数为:%d\n", g.vexnum, g.edgenum);*/

	/*printf("\n-----删除图G中无向边(x, y)-----\n");
	x = 'B', y = 'F';
	printf("删除顶点%c与顶点%c之间的一条无向边\n", x, y);
	printf("删除成功了吗?— %d(1代表成功,原图中有边(x, y),0代表失败,原图中不存在该边或者顶点x/顶点y不存在)\n", RemoveEdge(g, x, y));
	printf("\n");
	PrintG(g);
	CountDegree(g);
	printf("\n");
	printf("\n无向图G的顶点数为:%d,边数为:%d\n", g.vexnum, g.edgenum);*/

	/*printf("\n-----求图G中顶点x的第一个邻接点-----\n");
	x = 'E';
	printf("图G中顶点%c的第一个邻接点的顶点号为:%d(-1代表x没有邻接点或图中不存在x)\n", x, FirstNeighbor(g, x));*/

	/*printf("\n-----求图G中除y之外顶点x的下一个邻接点的顶点号-----\n");
	x = 'B', y = 'E';
	printf("图G中除顶点%c之外顶点%c的下一个邻接点的顶点号为:%d(-1代表x没有邻接点或图中不存在x)\n", x, y, NextNeighbor(g, x, y));*/

	return 0;
}

运行结果测试:
在这里插入图片描述

  • ①Adjacent(G,x,y):判断图G是否存在弧<x,y>。
    在这里插入图片描述
  • ②Neighbors(G,x):列出图G中与结点x邻接的弧。
    在这里插入图片描述
  • ③InsertVertex(G,x):在图G中插入顶点x。
    在这里插入图片描述
  • ④DeleteVertex(G,x):从图G中删除顶点x。
    在这里插入图片描述
  • ⑤Addedge(G,x,y):若无向边(x,y)或有向边<x,y>不存在,则向图G中添加该边。
    在这里插入图片描述
  • ⑥RemoveEdge(G,x,y):若无向边(x,y)存在,则从图G中删除该边。
    在这里插入图片描述
  • ⑦FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1。
    在这里插入图片描述
  • ⑧NextNeighbor(G,x,y):假设图G中顶点y是顶点x的一个邻接点,返回除y外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1。
    在这里插入图片描述

邻接表存储方法的特点

  1. 若G是无向图,则所需的存储空间为O(|V|+2|E|);若G为有向图,则所需的存储空间为O((|V|+|E|)。前者的倍数2是由于无向图中,每条边在邻接表中出现了两次。
  2. 对于稀疏图,采用邻接表表示将极大地节省存储空间。
  3. 在邻接表中,给定一顶点,能很容易地找出它的所有邻边,因为只需要读取它的邻接表。在邻接矩阵中,相同的操作则需要扫描一行,花费的时间为O(n)。但是,若要确定给定的两个顶点间是否存在边,则在邻接矩阵中可以立刻査到,而在邻接表中则需要在相应结点对应的边表中査找另一结点,效率较低。
  4. 在有向图的邻接表表示中,求一个给定顶点的出度只需计算其邻接表中的结点个数;但求其顶点的入度则需要遍历全部的邻接表。因此,也有人采用逆邻接表的存储方式来加速求解给定顶点的入度。当然,这实际上与邻接表存储方式是类似的。
  5. 图的邻接表表示并不唯一,因为在每个顶点对应的单链表中,各边结点的链接次序可以是任意的,它取决于建立邻接表的算法及边的输入次序。
  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值