基于C语言的数据结构_图 学习笔记

目录

09 数据结构 图

09.1 邻接矩阵的表示方法

09.2 邻接矩阵的实现方法

09.3 邻接表的表示方法

09.4 邻接表的实现

09.5 十字链表

09.6 邻接多重表


09 数据结构 图

前言:本节主要讲点概念性的东西 纯大白话

        在之前我们讲过了队列 栈 链表,对于这些结构我们可以把他们理解为是一些线性的数据结构,这种线性结构一般都是一对一的关系,每个元素都有唯一对应的前驱和后继,也可以说是一种线性的表。对于树 二叉树 则是一对多的关系,其指的是每个节点可以有多个节点。 而本节讲的图的结构是多对多的关系,在这种关系中任意两个节点都有可能产生关联,每个节点可以有多个前驱点和后继点,就类似于地图 交通网络图。 每个城市你可以想象成为一个点, 公路 铁路是边 把城市与城市全部关联起来了。 实际上图的应用非常的广泛,如电路分析 寻找最短路径,工程规划...

         接下来介绍有关图的一些概念:通常图可以用二元组来表示,图G分为两个部分 V E 。v用来表示顶点,E用来表示边。可以说图是由点和边构成的。V是顶点的集合 E是边的集合,V不可为空 可以没边但不能点也没有(道理就像吃草的牛,草呢!被牛吃了!牛呢!吃饱了就走了!)但要在编写代码层面上来说V可以为空 但应用上不可为空。

        一般来说图分为两种: 无向图 有向图,这里的向是指方向。 对于有向图就似单行道一般,只能向一个方向的走,那么无向图就类似双向道。

        我们可以用(v1 v2)来表示无向图顶点v1 v2之间的关系。 用<v1 v2>来表示有向图v1 v2的关系 v1-->v2 ,其中(v1 v2) = (v2 v1) <v1 v2> \= <v2 v1>。所以无向图G1的表示:V(G1)={V1 V2 V3 V4}  E(G1)={(V1 V2),(V1 V3),( V2 V3),(...), ...} 无向图的G2的表示V(G2)={V1 V2 V3 V4}  E(G2)={<V1 V2>,<V1 V3>,< V2 V3>,<...>, ...} 需要注意的几种特殊情况。绝大多数的图都是简单图,不允许出现自环这中特例 还有多重图(同一条边的图 不能出现两次或两次以上)

完全图的概念、图中任意两点都有一条边

        由此 运用数学的知识 已知点数 可以求得边的个数,若有n个顶点 则共有n(n-1)/2个边 ,这个是无向完全的图的边数,有向完全图的边数为n(n-1)       

        另外介绍网的概念:类似与城市之间的距离 消耗掉的时间 金钱 ,图的点与点之间也有类似的概念——权值。我们把带权值的图称为网。

         两个重要的概念:邻接关联。邻接是顶点与顶点之间的关系,如果两个顶点有共同边 我们把他们称为相互的邻接点,我们可以说v1连接V2 或 v2连接v1 。关联是指边和顶点的关系。对于邻接点的边,我们可以称 边关联v1和v2.

        :顶点的度指的是与该顶点相关联的边的数目,这里有一个握手定理,一个图的度数之和等于边的两倍。道理简单 在计算度之和时 每条边被计算了两次。

        在有向图中度又分为入度出度。入度是以对应点为终点 出度是以对应点为出发点。另外所有的顶点的出度和等于入度和,这也很好理解 有出就一定有入 因此相等。

        路径:持续的边(就是一个边和两外的一个边完全是连着的)的顶点连成的序列, 路径的长度是指边的数目。

        这里又引出几个概念!什么是回环路径;第一个顶个和最后一个顶点的相同。简单的回路是指除首与尾中间的各节点没有重复,若有重复则是非简单回路。简单路径:vi 到vj的各个顶点没有重复;子图:从图中取出若干个顶点以及若个条边所构成的图。生成子图:取出所有的顶点及若干条边。连通图:顶点vi vj之间有路径,不一定邻接,如果图中任意两点都是联通的,则可以叫做连通图。连通分量:对于无向图的最大连通子图(再添加一个点就不连通了)叫做连通分量。对于连通图的最大连通分量就是本身。对于有向图可分为强连通图强连通分量,vi到vj有路径 vj到vi也有路径。最大强连通子图就是再加一个点就是不是强连通图了。

09.1 邻接矩阵的表示方法

现在设计一种数据表示方法,要求能存储节点,节点的度,有向图的出度与入度 等信息...

无向图的表示

 无向图邻接矩阵的特点:沿对角线对称;第i行或第i列 非0元素的个数等于第i顶点的

有向图的表示

 有向图邻接矩阵的特点:不一定对称;第i行非零元素的个数等于第i个顶点的出度;第i列非零元素的个数等于第i个顶点的入度

网的表示

特点和有向图一样 不仅很容易的得到 节点元素的入度和出度,而且很容易看出路径权值。

09.2 邻接矩阵的实现方法

很显然为了存储矩阵我们需要一个二维数组,也需要一个一维的数组来存储结点信息,为了辅助数组的遍历操作我们需要两个元素来存储顶点的个数 边的条数。

无向图代码的创建

#include <stdio.h>
#include <stdlib.h>

#define MAX 100

struct AMG_Graph
{
	int vex_num, edge_num;

	char Vex[MAX];
	int Edge[MAX][MAX];
};

struct AMG_Graph *Create_AMG_Graph(void);
void Show_AMG_Graph(struct AMG_Graph *graph);
int search_vex(struct AMG_Graph *graph, char c);

int main(void)
{
	struct AMG_Graph *ud_graph;

	ud_graph = Create_AMG_Graph();
	Show_AMG_Graph(ud_graph);

	return 0;
}

struct AMG_Graph *Create_AMG_Graph(void)
{
	int i, j;
	char u, v;

	struct AMG_Graph *graph;

	graph = (struct AMG_Graph *)malloc(sizeof(struct AMG_Graph));

	printf("Please enter the number of vex: ");
	scanf("%d", &graph->vex_num);
	printf("Please enter the number of edge: ");
	scanf("%d", &graph->edge_num);

	while(getchar() != '\n');

	printf("Please enter vertex:\n");
	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i] = getchar();
		while(getchar() != '\n');
	}

	for(i = 0; i < graph->vex_num; i++)
	{
		for(j = 0; j < graph->vex_num; j++)
			graph->Edge[i][j] = 0;
	}

	while(graph->edge_num--)
	{
		printf("Please enter the vex that connect each other by edge:\n");
		u = getchar();
		while(getchar() != '\n');
		v = getchar();
		while(getchar() != '\n');

		i = search_vex(graph, u);
		j = search_vex(graph, v);

		if(i != -1 && j != -1)
			graph->Edge[i][j] = graph->Edge[j][i] = 1;
		else
		{
			printf("You have entered wrong vex, please enter again.\n");
			graph->edge_num++;
		}

	}

	return graph;
}

void Show_AMG_Graph(struct AMG_Graph *graph)
{
	int i, j;

	printf("Show the vex: \n");
	for(i = 0; i < graph->vex_num; i++)
		printf("%c ", graph->Vex[i]);
	printf("\n");

	printf("Show the adjacency matrices:\n");
	for(i = 0; i< graph->vex_num; i++)
	{
		for(j = 0; j < graph->vex_num; j++)
			printf("%d\t", graph->Edge[i][j]);
		printf("\n");
	}
}

int search_vex(struct AMG_Graph *graph, char c)
{
	int i;

	for(i = 0; i < graph->vex_num; i++)
	{
		if(c == graph->Vex[i])
			return i;
	}

	return -1;
}

有向图代码的创建
        在无向图代码的基础上删除修改    graph->Edge[i][j] = graph->Edge[j][i] = 1; 这部分即可。

#include <stdio.h>
#include <stdlib.h>

#define MAX 100

struct AMG_Graph
{
	int vex_num, edge_num;

	char Vex[MAX];
	int Edge[MAX][MAX];
};

struct AMG_Graph *Create_AMG_Graph(void);
void Show_AMG_Graph(struct AMG_Graph *graph);
int search_vex(struct AMG_Graph *graph, char c);

int main(void)
{
	struct AMG_Graph *ud_graph;

	ud_graph = Create_AMG_Graph();
	Show_AMG_Graph(ud_graph);

	return 0;
}

struct AMG_Graph *Create_AMG_Graph(void)
{
	int i, j;
	char u, v;

	struct AMG_Graph *graph;

	graph = (struct AMG_Graph *)malloc(sizeof(struct AMG_Graph));

	printf("Please enter the number of vex: ");
	scanf("%d", &graph->vex_num);
	printf("Please enter the number of edge: ");
	scanf("%d", &graph->edge_num);

	while(getchar() != '\n');

	printf("Please enter vertex:\n");
	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i] = getchar();
		while(getchar() != '\n');
	}

	for(i = 0; i < graph->vex_num; i++)
	{
		for(j = 0; j < graph->vex_num; j++)
			graph->Edge[i][j] = 0;
	}

	while(graph->edge_num--)
	{
		printf("Please enter the vex that connect each other by edge:\n");
		u = getchar();
		while(getchar() != '\n');
		v = getchar();
		while(getchar() != '\n');

		i = search_vex(graph, u);
		j = search_vex(graph, v);

		if(i != -1 && j != -1)
			graph->Edge[i][j] = 1;
		else
		{
			printf("You have entered wrong vex, please enter again.\n");
			graph->edge_num++;
		}

	}

	return graph;
}

void Show_AMG_Graph(struct AMG_Graph *graph)
{
	int i, j;

	printf("Show the vex: \n");
	for(i = 0; i < graph->vex_num; i++)
		printf("%c ", graph->Vex[i]);
	printf("\n");

	printf("Show the adjacency matrices:\n");
	for(i = 0; i< graph->vex_num; i++)
	{
		for(j = 0; j < graph->vex_num; j++)
			printf("%d\t", graph->Edge[i][j]);
		printf("\n");
	}
}

int search_vex(struct AMG_Graph *graph, char c)
{
	int i;

	for(i = 0; i < graph->vex_num; i++)
	{
		if(c == graph->Vex[i])
			return i;
	}

	return -1;
}

09.3 邻接表的表示方法

利用邻接矩阵的二维图行与列的关系,我们很容易得到图的度,入度与出度 ,这种数据结构关系是顺序存储的。你会发现为了组成矩阵,二维数组我们浪费了很多空间并非每个数组都赋有值。

那么有没有其他的像链式存储的对图表示的方法。回答:有.....邻接表。对于邻接表的学习我们要重点思考它是怎么存储表示图的度,出度和入度的,可以对比矩阵的表示方法。

无向图的邻接表

 特点:n个节点 e条边 则顶点表有n个节点,临接点表有2e个节点。

有向图的邻接表

 特点:n个节点 e条边 则顶点表有n个节点,临接点表有e个节点。顶点的出度为该顶点后单链表的节点数,入度要遍历邻接点表后统计个数。(如果你比较重视统计入度而非出度,你可以把箭头指向取反使用逆邻接表。)

09.4 邻接表的实现

有向图的邻接表

#include <stdio.h>
#include <stdlib.h>

#define MAX 100

struct AdjNode
{
	int index;
	struct AdjNode *next;
};

struct VexNode
{
	char node;
	struct AdjNode *first;
};

struct ALG_Graph
{
	int vex_num, edge_num;
	struct VexNode Vex[MAX];
};

struct ALG_Graph *Create_ALG_Graph(void);
int search_vex(struct ALG_Graph *graph, char c);
void create_adj_node_list(struct ALG_Graph *graph, int i, int j);
void Show_ALG_Graph(struct ALG_Graph *graph);


int main(void)
{
	struct ALG_Graph *d_graph;
	d_graph = Create_ALG_Graph();
	Show_ALG_Graph(d_graph);
	return 0;
}

struct ALG_Graph *Create_ALG_Graph(void)
{
	int i, j;
	char u, v;

	struct ALG_Graph *graph;

	graph = (struct ALG_Graph *)malloc(sizeof(struct ALG_Graph));

	printf("Please enter the number of vex: ");
	scanf("%d", &graph->vex_num);
	printf("Please enter the number of edge: ");
	scanf("%d", &graph->edge_num);
	while(getchar() != '\n');

	printf("Please enter vertex:\n");
	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i].node = getchar();
		while(getchar() != '\n');
	}

	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i].first = NULL;
	}

	while(graph->edge_num--)
	{
		printf("Please enter the vex that connect each other by edge:\n");
		u = getchar();
		while(getchar() != '\n');
		v = getchar();
		while(getchar() != '\n');

		i = search_vex(graph, u);
		j = search_vex(graph, v);

		if(i != -1 && j != -1)
			create_adj_node_list(graph, i, j);
		else
		{
			printf("You have entered wrong vex, please enter again.\n");
			graph->edge_num++;
		}
	}
	return graph;
}

int search_vex(struct ALG_Graph *graph, char c)
{
	int i;

	for(i = 0; i < graph->vex_num; i++)
	{
		if(c == graph->Vex[i].node)
			return i;
	}

	return -1;
}

void create_adj_node_list(struct ALG_Graph *graph, int i, int j)
{
	struct AdjNode *s = (struct AdjNode *)malloc(sizeof(struct AdjNode));
	s->index = j;
	s->next = graph->Vex[i].first;
	graph->Vex[i].first = s;
}

void Show_ALG_Graph(struct ALG_Graph *graph)
{
	int i;
	struct AdjNode *t;

	printf("Show the ALG Graph:\n");

	for(i = 0; i < graph->vex_num; i++)
	{
		printf("%c: ", graph->Vex[i].node);
		t = graph->Vex[i].first;
		while(t != NULL)
		{
			printf("%d ", t->index);
			t = t->next;
		}
		printf("\n");
	}
}

无向图的邻接表

#include <stdio.h>
#include <stdlib.h>

#define MAX 100

struct AdjNode
{
	int index;
	struct AdjNode *next;
};

struct VexNode
{
	char node;
	struct AdjNode *first;
};

struct ALG_Graph
{
	int vex_num, edge_num;
	struct VexNode Vex[MAX];
};

struct ALG_Graph *Create_ALG_Graph(void);
int search_vex(struct ALG_Graph *graph, char c);
void create_adj_node_list(struct ALG_Graph *graph, int i, int j);
void Show_ALG_Graph(struct ALG_Graph *graph);


int main(void)
{
	struct ALG_Graph *md_graph;
	md_graph = Create_ALG_Graph();
	Show_ALG_Graph(md_graph);

	return 0;
}

struct ALG_Graph *Create_ALG_Graph(void)
{
	int i, j;
	char u, v;

	struct ALG_Graph *graph;

	graph = (struct ALG_Graph *)malloc(sizeof(struct ALG_Graph));

	printf("Please enter the number of vex: ");
	scanf("%d", &graph->vex_num);
	printf("Please enter the number of edge: ");
	scanf("%d", &graph->edge_num);
	while(getchar() != '\n');

	printf("Please enter vertex:\n");
	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i].node = getchar();
		while(getchar() != '\n');
	}

	for(i = 0; i < graph->vex_num; i++)
	{
		graph->Vex[i].first = NULL;
	}

	while(graph->edge_num--)
	{
		printf("Please enter the vex that connect each other by edge:\n");
		u = getchar();
		while(getchar() != '\n');
		v = getchar();
		while(getchar() != '\n');

		i = search_vex(graph, u);
		j = search_vex(graph, v);

		if(i != -1 && j != -1){
			create_adj_node_list(graph, i, j);
            create_adj_node_list(graph, j, i);
        }
		else
		{
			printf("You have entered wrong vex, please enter again.\n");
			graph->edge_num++;
		}
	}
	return graph;
}

int search_vex(struct ALG_Graph *graph, char c)
{
	int i;

	for(i = 0; i < graph->vex_num; i++)
	{
		if(c == graph->Vex[i].node)
			return i;
	}

	return -1;
}

void create_adj_node_list(struct ALG_Graph *graph, int i, int j)
{
	struct AdjNode *s = (struct AdjNode *)malloc(sizeof(struct AdjNode));
	s->index = j;
	s->next = graph->Vex[i].first;
	graph->Vex[i].first = s;
}

void Show_ALG_Graph(struct ALG_Graph *graph)
{
	int i;
	struct AdjNode *t;

	printf("Show the ALG Graph:\n");

	for(i = 0; i < graph->vex_num; i++)
	{
		printf("%c: ", graph->Vex[i].node);
		t = graph->Vex[i].first;
		while(t != NULL)
		{
			printf("%d ", t->index);
			t = t->next;
		}
		printf("\n");
	}
}

09.5 十字链表

对于普通的邻接表,如果你重视出度你可以用有向图的邻接表,相反如果你重视入度你可以使用有向图的逆邻接表。那么有没有一种更优的解法 既容易看出入度也容易看出出度? 有 我们要在链表的基础上增加一些空间...——十字链表

十字链表由 节点表+边节点表(这里叫什么都可以 概念不是最重要的)
节点表由顶点,入弧+出弧 组成(入弧与出弧是指针)
边界点表由 尾+头+同头+同尾

09.6 邻接多重表

相对于无向图的邻接表,邻接多重表更关注边的一些操作。

猜你喜欢

转载自blog.csdn.net/shelter1234567/article/details/130387573