【图的介绍】

文章目录

      • 一、图的介绍
      • 二、图的定义和术语
      • 三、图的存储结构:
        • 邻接矩阵:
          • 邻接矩阵的优点:
          • 邻接矩阵的缺点:
        • 邻接表:
          • 邻接表优点:
          • 邻接表缺点:
        • 十字链表:
          • 十字链表的优点:
          • 十字链表的缺点:
        • 邻接多重表:

一、图的介绍

​ 图是一种比较复杂的数据结构,在线性表中数据元素之间仅有线性关系,每个元素只有一个直接前驱和直接后继(元素之间只存在一对一关系),在树形结构中元素之间有着明显的层次关系,每一层的元素只能和下层的多个元素有关系(元素之间存在一对多关系),而在图形结构中,任意两个结点之间都可能有关系(元素之间存在多对多关系)。

二、图的定义和术语

**顶点:**图中的数据元素被称为顶点,一般使用V表示图的顶点的有穷非空集合。

**弧:**两个顶点之间的关系记作,表示能从顶点v到达顶点w,也就是v能到w,但w不一定能到v,我们称v为弧尾或初始点,称w为弧头或终端点。

**有向图:**由弧+顶点构成的图叫有向图,也就是顶点之间是单行道。

**边:**两个顶点之间的关系记录(v,w),表示既能从v到w,也能从w到v,我们称v和w之间的关系是一条边。

**无向图:**由边+顶构成的图有无向图,顶点之间的双行道。

**注意:**一般使用G代表图,V顶点的集合,VR代表弧或边的集合,n代表顶点的数目,e代表边或弧的数目,我们不讨论顶点到自己的边或弧。

**完全图:**在无向图中,e的取值范围是0 ~ n/2(n-1),如果无向图的边的数量达到最大值这种无向图称称为完全图。

**有向完全图:**在有向图中,e的取值范围是0 ~ n(n-1),如果有向图的弧的数量达到最大值这种有向图称称为有向完全图。

**稀疏图和稠密图:**如果图中的边和弧很少,e

**权和网:**如果图中的顶点到另一个顶点需要代价(距离或耗费),那么在表示边或弧的时候需要附加数据,附加的数据就叫做权,带权的图通常被称为网,这也是互联网的由来。

**子图:**假定有两个图G1和G2,如果G1的顶点集合是G2的顶点集合的子集,且G1的边或弧集合是G2的边或弧集合的子集,则称G1是G2的子图。

**邻接点:**在无向图中如果有一条边(v,w),则v,w两个顶点互为邻接点,即v和w相邻接,边(v,w)依附于顶点v,w,或者说(v,w)和顶点v、w相关联。

**顶点v的度:**在无向图中与顶点v相关联的边的数量。

**顶点v的入度和出度:**在有向图中,以顶点v作为弧头的弧的数量称为顶点的入度,以顶点v作为弧尾的弧的数量称为顶点的出度。

**路径:**从顶点v到达顶点w所经历的顶点序叫做路径,路径的长度就是边或弧的数目。

**回路或环:**起始点和终点相同的路径称为回路或环。

**简单路径:**路径中顶点不重复出现的路径称为简单路径。

**简单回路或简单环:**起始点和终点相同且其余顶点不重复出现,被称为简单回路或简单环。

**连通图:**在无向图中,从顶点v到顶点w有路径,则称v和w是连通的,如果图中任意两个顶点都是连通的,则称图为连通图。

**连通分量:**G1和G2都是连通图,且G1是G2的子图,则称G1是G2的连通分量或极大连通子图。

强连通图:在有向图中,如果任意一对顶点都双向连通,则称图为强连通图。

强连通分量:G1和G2都是强连通图,且G1是G2的子图,则称G1是G2的强连通分量或极大强连通子图。

三、图的存储结构:

邻接矩阵:

​ 使用两个数组来存储数据元素(顶点)和数据元素之间的关系(边或弧)的信息。

​ 一维数组:用于存储顶点。

​ 二维数组:用于存储弧或边。

邻接矩阵的优点:

​ 访问速度快,方便计算结点的度、入度、出度。

邻接矩阵的缺点:

​ 顶点的容量有限,扩容非常麻烦,只适合存储稠密图,存储稀疏图时会有大量的内存浪费。

#include 
#include 
#include 

// 无向图
typedef struct Grahh
{
	size_t cal;	  // 顶点容量
	size_t cnt;	  // 顶点数量
	char *vertex; // 存储顶点的一维数组
	char **edge;  // 存储边的二维数组
} Graph;

Graph *create_graph(size_t cal)
{
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(char) * cal);
	graph->cal = cal;
	graph->cnt = 0;

	graph->edge = malloc(sizeof(char *) * cal);
	for (int i = 0; i < cal; i++)
	{
		graph->edge[i] = calloc(sizeof(char), cal);
	}

	return graph;
}

bool add_vertex_graph(Graph *graph, char vertex)
{
	if (graph->cnt >= graph->cal)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
		if (graph->vertex[i] == vertex)
			return false;
	}

	graph->vertex[graph->cnt++] = vertex;
	return true;
}

bool add_edge_graph(Graph *graph, char v1, char v2)
{
	int v1_index = -1, v2_index = -1;
	for (int i = 0; i < graph->cnt; i++)
	{
		if (graph->vertex[i] == v1)
			v1_index = i;
		if (graph->vertex[i] == v2)
			v2_index = i;
	}

	if (-1 == v1_index || -1 == v2_index || v1_index == v2_index)
		return false;

	graph->edge[v1_index][v2_index] = 1;
	graph->edge[v2_index][v1_index] = 1;
	return true;
}

// 查询顶点在一维数组中的位置,如果不存在则返回-1
int query_vertex_graph(Graph *graph, char vertex)
{
	for (int i = 0; i < graph->cnt; i++)
	{
		if (vertex == graph->vertex[i])
			return i;
	}
	return -1;
}

// 查询顶点第一个邻接点,如果不存在则返回'\0'
char first_vertex_graph(Graph *graph, char vertex)
{
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return '\0';

	for (int i = 0; i < graph->cnt; i++)
	{
		if (1 == graph->edge[index][i])
			return graph->vertex[i];
	}

	return '\0';
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
		graph->edge[index][i] = 0;
		graph->edge[i][index] = 0;
	}

	return true;
}

void _dfs_graph(Graph *graph, int index, bool *flags)
{
	if (!flags[index])
		return;

	printf("%c\n", graph->vertex[index]);
	flags[index] = false;

	for (int i = 0; i < graph->cnt; i++)
	{
		if (1 == graph->edge[index][i])
			_dfs_graph(graph, i, flags);
	}
}

// 深度优先遍历,与树的前序遍历类似
void dfs_graph(Graph *graph)
{
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		_dfs_graph(graph, i, flags);
}

// 广度优先遍历,与树的层序遍历一样,需要队列结构配合
void bfs_graph(Graph *graph)
{
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;

	for (int i = 0; i < graph->cnt; i++)
	{
		if (flags[i])
		{
			flags[i] = false;
			queue[rear++] = i;
		}

		while (front != rear)
		{
			int index = queue[front++];
			printf("%c ", graph->vertex[index]);

			for (int j = 0; j < graph->cnt; j++)
			{
				if (1 == graph->edge[index][j] && flags[j])
				{
					flags[j] = false;
					queue[rear++] = j;
				}
			}
		}
	}
}

// 销毁图
void destroy_graph(Graph *graph)
{
	for (int i = 0; i < graph->cnt; i++)
	{
		free(graph->edge[i]);
	}
	free(graph->edge);
	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
	Graph *graph = create_graph(5);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');

	add_edge_graph(graph, 'A', 'C');
	add_edge_graph(graph, 'A', 'D');
	add_edge_graph(graph, 'C', 'B');
	add_edge_graph(graph, 'B', 'D');
	// del_vertex_graph(graph,'C');
	printf("------------------------\n");
	for (int i = 0; i < graph->cnt; i++)
	{
		for (int j = 0; j < graph->cnt; j++)
		{
			printf("%hhd ", graph->edge[i][j]);
		}
		printf("\n");
	}
	// destroy_graph(graph);
	// printf("%c\n",first_vertex_graph(graph,'D'));
	// dfs_graph(graph);
	bfs_graph(graph);
	return 0;
}

邻接表:

​ 采用一维数组加单向链表的方式存储存储。

​ 一维数组:用于存储顶点和单向链表的头指针。

​ 单向链表:存储着从该顶点出发的弧或边。

邻接表优点:

​ 与邻接矩阵相比,它可以节约内存,有多少条边或弧就创建多少个链表节点,比较适合存储稀疏图。

邻接表缺点:

​ 计算入度、删除节点时比较麻烦。

#include 
#include 
#include 

// 弧
typedef struct Bow
{
	int dest;
	struct Bow *next;
} Bow;

// 顶点
typedef struct Vertex
{
	char data;
	Bow first; // 单链表的空白头节点
} Vertex;

// 有向图
typedef struct Graph
{
	Vertex *vertex;
	size_t cal;
	size_t cnt;
} Graph;

// 创建图
Graph *create_graph(size_t cal)
{
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(Vertex) * cal);
	graph->cal = cal;
	graph->cnt = 0;
	return graph;
}

// 添加顶点
bool add_vertex_graph(Graph *graph, char vertex)
{
	if (graph->cnt >= graph->cal)
		return false;

	// 顶点不能重复
	for (int i = 0; i < graph->cnt; i++)
		if (vertex == graph->vertex[i].data)
			return false;

	graph->vertex[graph->cnt].data = vertex;
	graph->vertex[graph->cnt++].first.next = NULL;
	return true;
}

// 查询顶点
int query_vertex_graph(Graph *graph, char vertex)
{
	for (int i = 0; i < graph->cnt; i++)
	{
		if (graph->vertex[i].data == vertex)
			return i;
	}

	return -1;
}

// 添加弧
bool add_bow_graph(Graph *graph, char bow_tail, char bow_head)
{
	int index_tail = query_vertex_graph(graph, bow_tail);
	if (-1 == index_tail)
		return false;

	int index_head = query_vertex_graph(graph, bow_head);
	if (-1 == index_head)
		return false;

	Bow *bow = malloc(sizeof(Bow));
	bow->dest = index_head;

	bow->next = graph->vertex[index_tail].first.next;
	graph->vertex[index_tail].first.next = bow;
	return true;
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	if (NULL != graph->vertex[index].first.next)
	{
		Bow *bow = graph->vertex[index].first.next;
		graph->vertex[index].first.next = bow->next;
		free(bow);
	}
	graph->vertex[index].data = '\0';

	for (int i = 0; i < graph->cnt; i++)
	{
		for (Bow *bow = &graph->vertex[i].first; NULL != bow->next; bow = bow->next)
		{
			if (index == bow->next->dest)
			{
				Bow *tmp = bow->next;
				bow->next = tmp->next;
				free(tmp);
			}
		}
	}
	return true;
}

// 计算入度,计算出有多个弧的弧头是某顶点
int indegree_vertex_graph(Graph *graph, char vertex)
{
	// 打到顶点,遍历所有弧,dest等于vertex下的弧有多少条,如果找不到则返回-1
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int indegree = 0;
	for (int i = 0; i < graph->cnt; i++)
	{
		Bow *bow = graph->vertex[i].first.next;
		while (NULL != bow)
		{
			if (index == bow->dest)
				indegree++;
			bow = bow->next;
		}
	}

	return indegree;
}

// 计算出度
int outdegree_vertex_graph(Graph *graph, char vertex)
{
	// 找到顶点,从它出发的弧有多少条,如果找不到则返回-1
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int outdegree = 0;
	Bow *bow = graph->vertex[index].first.next;
	while (NULL != bow)
	{
		outdegree++;
		bow = bow->next;
	}

	return outdegree;
}

// 深度优先遍历
void _dfs_graph(Graph *graph, int index, bool *flags)
{
	flags[index] = false;
	printf("%c ", graph->vertex[index].data);

	Bow *bow = graph->vertex[index].first.next;
	while (NULL != bow)
	{
		if (flags[bow->dest])
			_dfs_graph(graph, bow->dest, flags);

		bow = bow->next;
	}
}

void dfs_graph(Graph *graph)
{
	// 参考昨天邻接矩阵的业务逻辑
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		if (flags[i])
			_dfs_graph(graph, i, flags);
	printf("\n");
}

// 广度优先遍历
void bfs_graph(Graph *graph)
{
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;

	for (int i = 0; i < graph->cnt; i++)
	{
		if (flags[i])
		{
			flags[i] = false;
			queue[rear++] = i;
		}

		while (front != rear)
		{
			int index = queue[front++];
			printf("%c ", graph->vertex[index].data);

			Bow *bow = graph->vertex[index].first.next;
			while (NULL != bow)
			{
				if (flags[bow->dest])
				{
					flags[bow->dest] = false;
					queue[rear++] = bow->dest;
				}
				bow = bow->next;
			}
		}
	}
	printf("\n");
}

// 销毁图
void destroy_graph(Graph *graph)
{
	for (int i = 0; i < graph->cnt; i++)
	{
		Bow *bow = &graph->vertex[i].first;
		while (NULL != bow->next)
		{
			Bow *tmp = bow->next;
			bow->next = tmp->next;
			free(tmp);
		}
	}
	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
	Graph *graph = create_graph(10);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');

	add_bow_graph(graph, 'A', 'D');
	add_bow_graph(graph, 'A', 'E');
	add_bow_graph(graph, 'A', 'C');
	add_bow_graph(graph, 'C', 'B');
	add_bow_graph(graph, 'B', 'D');
	// add_bow_graph(graph,'E','D');
	/*
	del_vertex_graph(graph,'E');
	for(int i=0; icnt; i++)
	{
		printf("%d %c first->%p",i,graph->vertex[i].data,graph->vertex[i].first.next);
		for(Bow* bow=graph->vertex[i].first.next; NULL!=bow; bow=bow->next)
		{
			printf(" %p dest:%d ",bow,bow->dest);
		}
		printf("\n");
	}
	printf("%d\n",outdegree_vertex_graph(graph,'B'));
	printf("%d\n",indegree_vertex_graph(graph,'A'));
	*/
	dfs_graph(graph);
	bfs_graph(graph);
	destroy_graph(graph);
	return 0;
}

十字链表:

​ 采用一维数组加两个十字链表进行存储,之所以叫十字链表,是因为它的链表弧节点会被两个链表指向,同时弧节点有两个指针,指向后续的弧节点。

​ 一维数组:存储了顶点数据,两个链表头指针。

​ 出度链表:指向的是以该结点作为弧尾的弧节点。

​ 入度链表:指向的是以该结点作为弧头的弧节点。

十字链表的优点:

​ 与邻接矩阵相比,它更节约内存,与邻接表相比它更方便计算顶点的出度。

十字链表的缺点:

​ 删除顶点和删除弧时比较麻烦,也可以表示与实现无向图,但更合适表示与实现有向图。

#include 
#include 
#include 

// 弧
typedef struct Bow
{
	int tailvex;	   // 弧尾的顶点下标
	int headvex;	   // 弧头的顶点下标
	struct Bow *tlink; // 指向相同弧尾的弧节点
	struct Bow *hlink; // 指向相同弧头的弧节点
} Bow;

// 顶点
typedef struct Vertex
{
	char data;
	Bow *inlink;
	Bow *outlink;
} Vertex;

// 图
typedef struct Graph
{
	Vertex *vertex;
	size_t cal;
	size_t cnt;
} Graph;

// 创建图
Graph *create_graph(size_t cal)
{
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(Vertex) * cal);
	graph->cal = cal;
	graph->cnt = 0;
	return graph;
}
// 查询顶点下标
int query_vertex_graph(Graph *graph, char vertex)
{
	for (int i = 0; i < graph->cnt; i++)
		if (vertex == graph->vertex[i].data)
			return i;

	return -1;
}

// 添加顶点
bool add_vertex_graph(Graph *graph, char vertex)
{
	// 如果顶点已经满,或顶点已经存在则返回false
	if (graph->cnt >= graph->cal || -1 != query_vertex_graph(graph, vertex))
		return false;

	graph->vertex[graph->cnt].data = vertex;
	graph->vertex[graph->cnt].inlink = NULL;
	graph->vertex[graph->cnt++].outlink = NULL;
	return true;
}

// 添加弧
bool add_bow_graph(Graph *graph, char tailvex, char headvex)
{
	// 如果弧头或弧尾的顶点不存在则添加失败
	int tailindex = query_vertex_graph(graph, tailvex);
	int headindex = query_vertex_graph(graph, headvex);
	if (-1 == tailindex || -1 == headindex)
		return false;

	// 创建一个弧
	Bow *bow = malloc(sizeof(Bow));
	bow->tailvex = tailindex;
	bow->headvex = headindex;

	bow->tlink = graph->vertex[tailindex].outlink;
	graph->vertex[tailindex].outlink = bow;

	bow->hlink = graph->vertex[headindex].inlink;
	graph->vertex[headindex].inlink = bow;

	return true;
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
	// 找到顶点的下标,如果找不到则返回false
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	// 以它作为弧尾的弧要全部断开
	graph->vertex[index].inlink = NULL;

	// 以它作为弧头的弧要全部删除
	for (int i = 0; i < graph->cnt; i++)
	{
		Bow *out = graph->vertex[i].outlink;
		while (NULL != out && NULL != out->tlink)
		{
			if (out->tlink->headvex == index)
			{
				Bow *tmp = out->tlink;
				out->tlink = tmp->tlink;
				free(tmp);
			}
			out = out->tlink;
		}
		if (NULL != graph->vertex[i].outlink &&
			index == graph->vertex[i].outlink->headvex)
		{
			Bow *tmp = graph->vertex[i].outlink;
			graph->vertex[i].outlink = tmp->tlink;
			free(tmp);
		}
	}
	return true;
}

// 删除弧
bool del_bow_graph(Graph *graph, char tailvex, char headvex)
{
	// 找到弧头、弧尾顶点的下标,如果有一个找不到就返回false
	int tailindex = query_vertex_graph(graph, tailvex);
	int headindex = query_vertex_graph(graph, headvex);
	if (-1 == tailindex || -1 == headindex)
		return false;

	// 删除弧,并且要保证两个链表不断开
	Bow *out = graph->vertex[tailindex].outlink;
	while (NULL != out && NULL != out->tlink)
	{
		if (out->tlink->headvex == headindex)
		{
			out->tlink = out->tlink->tlink;
			break;
		}
		out = out->tlink;
	}
	if (NULL != graph->vertex[tailindex].outlink &&
		headindex == graph->vertex[tailindex].outlink->headvex)
	{
		graph->vertex[tailindex].outlink = graph->vertex[tailindex].outlink->tlink;
	}

	Bow *in = graph->vertex[headindex].inlink;
	while (NULL != in && NULL != in->hlink)
	{
		if (in->hlink->tailvex == tailindex)
		{
			Bow *tmp = in->hlink;
			in->hlink = tmp->hlink;
			free(tmp);
			break;
		}
		in = in->hlink;
	}

	if (NULL != graph->vertex[headindex].outlink &&
		tailindex == graph->vertex[headindex].outlink->tailvex)
	{
		Bow *tmp = graph->vertex[headindex].inlink;
		graph->vertex[headindex].inlink = tmp->hlink;
		free(tmp);
	}
	return true;
}

// 计算入度
int indegree_vertex_graph(Graph *graph, char vertex)
{
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int indegree = 0;
	Bow *in = graph->vertex[index].inlink;
	while (NULL != in)
	{
		indegree++;
		in = in->hlink;
	}

	return indegree;
}
// 计算出度
int outdegree_vertex_graph(Graph *graph, char vertex)
{
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return -1;

	int outdegree = 0;
	Bow *out = graph->vertex[index].outlink;
	while (NULL != out)
	{
		outdegree++;
		out = out->tlink;
	}
	return outdegree;
}

// 深度优先
void _dfs_graph(Graph *graph, int index, bool *flags)
{
	printf("%c ", graph->vertex[index].data);
	flags[index] = false;

	Bow *out = graph->vertex[index].outlink;
	while (NULL != out)
	{
		if (flags[out->headvex])
			_dfs_graph(graph, out->headvex, flags);
		out = out->tlink;
	}
}

void dfs_graph(Graph *graph)
{
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		if (flags[i])
			_dfs_graph(graph, i, flags);
	printf("\n");
}

// 广度优先
void bfs_graph(Graph *graph)
{
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->cnt], front = 0, rear = 0;
	for (int i = 0; i < graph->cnt; i++)
	{
		if (flags[i])
		{
			queue[rear++] = i;
			flags[i] = false;
		}
		while (front != rear)
		{
			int index = queue[front++];
			printf("%c ", graph->vertex[index].data);

			Bow *out = graph->vertex[index].outlink;
			while (NULL != out)
			{
				if (flags[out->headvex])
				{
					queue[rear++] = out->headvex;
					flags[out->headvex] = false;
				}
				out = out->tlink;
			}
		}
	}
	printf("\n");
}

// 销毁图
void destroy_graph(Graph *graph)
{
	for (int i = 0; i < graph->cnt; i++)
		del_vertex_graph(graph, graph->vertex[i].data);

	free(graph->vertex);
	free(graph);
}

int main(int argc, const char *argv[])
{
	Graph *graph = create_graph(10);
	add_vertex_graph(graph, 'A');
	add_vertex_graph(graph, 'B');
	add_vertex_graph(graph, 'C');
	add_vertex_graph(graph, 'D');
	add_vertex_graph(graph, 'E');
	add_bow_graph(graph, 'A', 'D');
	add_bow_graph(graph, 'A', 'E');
	add_bow_graph(graph, 'A', 'C');
	add_bow_graph(graph, 'C', 'B');
	add_bow_graph(graph, 'B', 'D');
	add_bow_graph(graph, 'E', 'D');
	dfs_graph(graph);
	bfs_graph(graph);
	destroy_graph(graph);
	// del_vertex_graph(graph,'D');
	// del_bow_graph(graph,'A','D');
	/*
	printf("%d\n",indegree_vertex_graph(graph,'B'));
	for(int i=0; icnt; i++)
	{
		printf("vex:%c",graph->vertex[i].data);
		printf("\n\t");
		Bow* out = graph->vertex[i].outlink;
		while(NULL != out)
		{
			printf("%d->%d ",out->tailvex,out->headvex);
			out = out->tlink;
		}
		printf("\n\t");
		Bow* in= graph->vertex[i].inlink;
		while(NULL != in)
		{
			printf("%d<-%d ",in->headvex,in->tailvex);
			in = in->hlink;
		}
		printf("\n");
	}

	*/
	return 0;
}

邻接多重表:

​ 是对邻接表一种扩展,在邻接表边的基础上,增加了访问标记、组成边的两个顶点的下标、指向依附这两个顶点的下一条边的指针。

​ 使用邻接表时,它的一条边的两个节点,分别在第i和第j的两个链表中,这给图的操作带来不便,而邻接多重表的链表节点增加各项成员,方便对图的操作。

你可能感兴趣的:(c语言,数据结构,学习)