• 数据结构-图(C语言)


    图的基本概念

    定义: 图(Graph)是一种非线性数据结构,形式化描述为:
    Graph = (V, R)
    其中,
    V={Vi | Vi属于datatype, i = 0,1,2,…n-1}是图中元素Vi(称为顶点,Vertex)的集合,
    n=0时,V为空集。
    R={ | Vi, Vj属于V, 且p(Vi, Vj)存在}是图中顶点之间的关系集,P(Vi, Vj)
    为顶点Vi到Vj之间是否存在路径的判定条件,即若Vi到Vj之间的路径存在,则关系
    属于R.

    分类:
    有向图 弧

    无向图 边

    网 :
    若在图的关系附加一个权值w
    w
    Vi ------> Vj
    w称为弧或边上的权。带权的图称为网。权w的具体含义 图在不同的应用领域来确定。
    如:顶点表示城市,权w可以视为两个城市之间距离等等。

    顶点的度:
    顶点边或弧的条数。

    连通图 和 非连通图

    路径:
    从某个顶点到其他顶点是否存在路径

    图的存储结构

    “数组表示法”
    “邻接表”

    “十字链表”
    “邻接多重表”

    数组表示法 又称为 “邻接矩阵”
    G = (V, R)
    可以用两个数组来存储图G:
    一个一维数组存储图G中的各个顶点。
    V 用另外一个二维数组A来存储图G中顶点之间的关系R.
    第i行,表示 以顶点V[i]为起点
    第j列, 表示 以顶点V[j]为终点

    图中的数据类型:

    图中顶点类型: 
    				typedef char Vtype;
    			权值的类型 : 
    				typedef int Adjtype;
    				
    			//图中顶点的最大数目  
    				#define MAXN 100 
    				
    			图的类型: 
    				typedef struct graph 
    				{
    					Vtype V[MAXN]; //一维数组来存储顶点
    					
    					Adjtype A[MAXN][MAXN]; //二维数组来存储顶点与顶点之间的关系
    											"邻接矩阵"
    											
    					int vexnum; //图中有效的顶点个数 
    					int arcnum; //图中有效边的条数 
    					//......
    				
    				}Graph;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    创建邻接矩阵
    需要写一个查找顶点在一维数组中的下标

    //找到某一个顶点对应的下标
    int Find_V_index(Graph* g, Vtype vex)
    {
    	int i;
    	for (i = 0; i < g->vexnum; i++)
    	{
    		if (g->V[i] == vex)
    		{
    			//找到了
    			return i;
    		}
    	}
    
    	return -1;//表示没有找到
    }
    
    Graph* Create_Graph(void)
    {
    	Graph* g = malloc(sizeof(Graph));
    	g->arcnum = 0;
    	g->vexnum = 0;
    	Vtype d;
    	while (1)
    	{
    		//约定当用户输入为‘#’表示结束 
    		scanf("%c", &d);
    		if (d == '#')
    		{
    			break;
    		}
    		g->V[g->vexnum] = d;
    		g->vexnum++;
    	}
    
    	//过滤掉'\n'
    	getchar();
    	
    	//将二维数组中,有效区域数值全部初始化为无穷大 ‘
    	int i, j;
    	for (i = 0; i < g->vexnum; i++)
    	{
    		for (j = 0; j < g->vexnum; j++)
    		{
    			g->A[i][j] = Very_Big;
    		}
    	}
    	Vtype s, e;
    	int s_i, e_i; //s和e分别对应的下标
    	Adjtype w;
    	while (1)
    	{
    		scanf("%c%c%d", &s, &e, &w);
    		if (s == '#' && e == '#' && w == 0)
    		{
    			break;
    		}
    
    		//先找到s对应在邻接矩阵中下标
    		s_i = Find_V_index(g, s);
    
    		//再找到e对应在邻接矩阵中下标 
    		e_i = Find_V_index(g, e);
    
    
    		g->A[s_i][e_i] = w;
    
    		//过滤掉'\n'
    		getchar();
    	}
    
    	return g;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    邻接表
    链式结构来存储图
    所谓邻接表,是将图中每一个顶点v和由v发出的弧
    或边构成单链表。邻接表是图的一种链式存储结构。

    数据类型:

    			typedef char Vtype; //顶点的类型 
    			typedef int Adjtype; //权值的类型 
    			
    			typedef struct bian 
    			{
    				int stop_index; //终点的下标
    				Adjtype w; //权值 
    				struct bian * next; //下一条边的指针
    			}Bian;
    			
    			//顶点元素的结构体 
    			typedef struct vertex 
    			{
    				Vtype data; //顶点的数据域
    				
    				Bian * first; //指向由该顶点为起点发出的第一条边
    				
    			}Vertex;
    			
    			//图中顶点元素的最大数目 
    			#define MAXN 100
    			
    			//图的类型 
    			Vertex Graph[MAXN];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    创建邻接表
    与创建邻接矩阵思路一样,只是插入数据时用的是链表的思想

    //找起点元素下标
    static int Find_index(Graph* G, Vtype s)
    {
    	int i;
    	for (i = 0; i < G->vexn; i++)
    	{
    		if (G->g[i].data == s)
    		{
    			return  i;
    		}
    	}
    	return -1;
    }
    
    
    //图初始化
    Graph* Init_Graph(void)
    {
    	Graph* G = malloc(sizeof(*G));
    	G->arcn = G->vexn = 0;
    
    	Vtype d;
    
    	while (1) //键盘输入数组
    	{
    		scanf("%c", &d);
    
    		if (d == '#')
    		{
    			break;
    		}
    		G->g[G->vexn].data = d;
    		G->g[G->vexn].first = NULL;
    		G->vexn++;
    	}
    	getchar(); //过滤\n
    
    	//构造顶点发出的边组成的各个单链表
    	Vtype s, e;
    	Adjtype w;
    	while (1)
    	{
    		scanf("%c%c%d", &s, &e, &w);
    
    		if (s == '#')
    		{
    			break;
    		}
    		Bian* p = malloc(sizeof(*p));
    		p->next = NULL;
    		p->stop_index = Find_index(G, e); //终点元素下标
    		p->w = w;
    
    		int s_i= Find_index(G, s); //起点元素下标
    
    		if (G->g[s_i].first == NULL) //初始为空
    		{
    			G->g[s_i].first = p;
    		}
    		else
    		{
    			//找到插入位置进行插入操作
    			Bian* pk = G->g[s_i].first;
    			Bian* pr = NULL;
    			while (pk)
    			{
    				if (pk->stop_index > p->stop_index) 
    				{
    					break;
    				}
    				pr = pk;
    				pk = pk->next;
    			}
    
    			if (pk == NULL) //尾插
    			{
    				pr->next = p;
    			}
    			else if (pk == G->g[s_i].first) //头插
    			{
    				p->next = pk;
    				G->g[s_i].first = p;
    			}
    			else
    			{
    				pr->next = p;
    				p->next = pk;
    			}
    		}
    
    		//过滤掉\n
    		getchar();
    	}
    	return G;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
  • 相关阅读:
    场景应用:线程池的队列大小你通常怎么设置?
    在Vue 3中加载本地图片和其他静态资源
    软件设计原则 1小时系列 (C++版)
    Node.js躬行记(24)——低代码
    一短文读懂编译型与解释型编程语言
    java File、io篇
    java多线程基础技术
    Web 前端基础操作小结
    如何按照洁净区不同等级选择不同流量的粒子计数器设备?
    CPU三级缓存原理与优化
  • 原文地址:https://blog.csdn.net/weixin_46836491/article/details/126081590