数据结构-图的创建(邻接矩阵,邻接表)C语言实现

2023-11-17

图的定义:

图(Graph)G由两个集合V和E组成,记为:G=(V,E),其中V是顶点的有穷非空集合(其实就是顶点),E是V中顶点偶对的有穷集合(就是边)。V(G)和E(G)通常分别表示图G的顶点集合以及边集合,E(G)可以为空集合,但是此时的图只有顶点,没有边。
分类:

  • 有向图:在有向图中,顶点对<V,W>是有序的,表示它从V到W的一条有向边。所以,<W,V>表示的和<V,W>不同的边,顶点的指向不同。有向图中顶点对用尖括号表示<>,<x,y>,x是有向边的始点,y是有向边的终点,称x为弧尾,y为弧头。
  • 无向图:在无向图中,顶点对(V,W)是无序的,表示V到W的一条边,这条边没有特定的方向即,(V,W)和(W,V)表示同一条边。和有向图不同的是,无向图的表示方法是用圆括号括起来()。

下面是图的示例:
在这里插入图片描述

图的存储结构:

  • 邻接矩阵:邻接矩阵(Adjacency Matrix)是表示顶点之间相邻关系的矩阵。设G=(V,E)是一个图,其中V={v1,v2,…,vn}。G的邻接矩阵是一个具有下列性质的n阶方阵:
    ①对无向图而言,邻接矩阵一定是对称的,而且主对角线一定为零(在此仅讨论无向简单图),副对角线不一定为0,有向图则不一定如此。
    ②在无向图中,任一顶点i的度为第i列(或第i行)所有非零元素的个数,在有向图中顶点i的出度为第i行所有非零元素的个数,而入度为第i列所有非零元素的个数。
    ③用邻接矩阵法表示图共需要n^2个空间,由于无向图的邻接矩阵一定具有对称关系,所以扣除对角线为零外,仅需要存储上三角形或下三角形的数据即可,因此仅需要n(n-1)/2个空间。
    声明一下,上述邻接矩阵的定义的描述来自于百度百科,可能大家第一遍看的时候不是那么理解,这里整那么多 什么i啊,什么度啊,还有什么空间啥的,整的大家比较烦,那么就让我这个小菜鸡来给大家捋一捋。
    首先,看这个名词邻接矩阵,不就是元素与元素相邻形成一个矩阵么,这时,希望大家,脑海中有一个矩阵的画面。这里面就存放了一些数据,至于是什么数据,后面会给大家讲到。
    其次,我们现在就要给脑海中这个矩阵放点东西了。好,我们想啊,一个图用邻接矩阵表示出来,这个矩阵中无非就是放了些从一个顶点"走"到另外一个顶点,要花去的"费用"嘛,这个费用就是我们的权重,那么显然这个矩阵中的数据就是顶点之间的权重。那么光有权重在里面我们不好理解,那么我们就在这个矩阵的最上方以及最左方加上每个元素的下标或者标号,这个我们看的时候就可以找到对应元素的权重了,实际操作中是不需要的。也就是说,我们现在这个矩阵,相当于变成了表格了,上面和左边是坐标,下面那一块全是数据(权重),此时这个表格就是我们所说的 ,不知道大家理解没有,如果没有的话,可以在草稿纸上画画,便于理解。
    接下来,讲讲我们的干货了。如何实现呢。
    邻接矩阵的实现
    思路(接下来,会以非常平常的语气,来讲述,因为本身我就是个小菜鸡,水平有限,还请见谅!)
    大家想啊,我要对这个图要进行一些操作,那么我们首先要做的就是初始化一个图,初始化什么样的一个图呢,初始化一个只有顶点,没有边的图,因为这样我们才能把权重放入图中。
    好,从这个出发点想,这个图要有什么要素呢,,,,,要有顶点数,边数,以及存放顶点的一些数据,还有顶点之间的权重,那么用什么来表示这些要素呢,顶点数,边数,自然是用整形类型的变量来表示咯,那顶点数据用什么来存放,因为顶点数是一维的,所以我们采用数组的存储形式存放顶点数据 。最后一个,权重,,,emm想想它的定义,从一个顶点到另外一个顶点所要花去的"费用",自然想到的就是二维数组咯。
    好了,废话讲到这里,接下来,干货来了,代码奉上
    再声明一下,代码中也会有一些注释,还有一些思路,都是本菜鸡写的,写的不好的地方,恳求大家指出,谢谢!!!
#pragma once
# include <stdio.h>
# include <stdlib.h>
# define MaxVertexnum 100
typedef int ElemType;
//邻接矩阵
typedef struct graph
{
 	int Nv;          //顶点数
 	int Ne;          //边数
	int Data[MaxVertexnum];      //存放顶点数据
 	int Weight[MaxVertexnum][MaxVertexnum];  //存放权重
}graph,*MGraph;
typedef struct edge
{
 	int V1, V2;  //V1到V2
 	int Weight;  //边用来存放权重
}*Edge;
//初始化有Vertnex个顶点的图
void InitGraph(MGraph G, int Vertnex)
{
 	G->Nv = Vertnex;   //有Vertnex个顶点
 	G->Ne = 0;     //边数为0
 //初始化每条边的权重
 	for (int i = 0; i < Vertnex; i++)
 	{
  		for (int j = 0; j < Vertnex; j++)
  			G->Weight[i][j] = 0;
 	}
}
//插入边的权重
void InsertEdge(MGraph G,Edge E)
{
 	G->Weight[E->V1][E->V2] = E->Weight;
 	G->Weight[E->V2][E->V1] = E->Weight;
}
  • 邻接表:邻接表是图的一种链式存储结构。在邻接表中,对图中每个顶点v建立一个单链表,把与v相邻接的顶点放在这个链表中。邻接表中的每个单链表的结点存放有关顶点的信息,把这个结点看成这个链表的表头,其余结点存放有关边的信息,这样邻接表便有两部分组成:表头结点和边表

表头结点表:由所有的表头结点以顺序结构的形式存储以便计算机随机访问任一顶点的边链表 。表头结点包括**数据域和链域(指向边表指针)**两部分组成。

边表:
边链表中边结点包括邻接点域,数据域,链域三部分。邻接点域指的是指向同类的指针,数据域指的是存放与边相关的一些信息(权重),链域指的是与头结点链域中指针所指向的指针域。
相信大家对于这个理解还是比较的深刻的,我这个小菜鸡就不瞎哔哔了^ - ^。
现在脑子里应该有这样一副画面,有一个长方形块,这个长方形块有若干个小块组成,每个小块有两个部分,第一部分是数据域,第二部分是指针域,这个指针域就指向边表。
代码奉上:

typedef struct EdgeNode   //边结点
{
 	int weight;     //存放权重
 	struct EdgeNode *next;  //指向边结点
 	int local;     //该边指向的顶点
}EdgeNode;
/*边链表中边结点包括邻接点域,数据域,链域,邻接点域指示与顶点v邻接的位置(指针),数据域存放与边相关的数据,如:权重。链域指示与顶点v
邻接的下一条边的结点*/
typedef struct VNode   //顶点结点
{
 	int data;     //顶点数据
 	EdgeNode *firstVertnex;  //指向边
}VNode,AdjList[MaxVertexnum]; 
/*表头结点表:由所有表头结点以顺序结构的形式存储,以便可以随机访问任一顶点的边链表
表头结点包括数据域,链域,数据域存放顶点的一些数据,链域用于指向链表中第一个结点(即与定点v邻接的第一个邻接点)*/
typedef struct Adjgraph
{
 	int Nv;
 	int Ne;
 	AdjList List;
}AdjMGraph;
void InitAdjGraph(AdjMGraph* G)
{
 	printf("请输入顶点数和边数(空格分隔)!\n");
 	int Nv, Ne;
 	scanf("%d %d", &G->Nv, &G->Ne);
 	printf("请输入顶点数据:\n");
 	for (int i = 1; i <= G->Nv; i++)
 	{
  		printf("第%d个顶点数据为:\n",i);
  		scanf("%d", &G->List[i].data);   //收入每一个顶点的数据
  		G->List[i].firstVertnex = NULL;   //每个顶点的指针域置为空
 	}
 	getchar();
}
/*接下来要进行放入有效数据,把权重放入邻接表中,由于是生成无向图,所以此时要生成两个结点*/
void CreateAdjGraph(AdjMGraph *G)
{
 	printf("一共有%d条边!\n", G->Ne);
 	printf("请输入两个顶点以及二者之间的权重(中间用空格分隔):\n");
 	int W, V,weight;
 	for (int i = 1; i <= G->Ne; i++)
 	{
  		scanf("%d %d %d", &W, &V,&weight);
  		EdgeNode *p = (EdgeNode*)malloc(sizeof(EdgeNode));
  		p->local = V; 
 		p->weight = weight;
  		p->next = G->List[W].firstVertnex;
  		G->List[W].firstVertnex= p;
  
  
  		EdgeNode *q = (EdgeNode*)malloc(sizeof(EdgeNode)); 
  		q->local = W;
  		q->weight = weight;
  		q->next = G->List[V].firstVertnex;
 	 	G->List[V].firstVertnex = q;
  
 	}
}

打印出邻接矩阵:

void PrintAdjGraph(AdjMGraph *G)
{
 	printf("将打印出邻接表的示意图!\n");
 	for (int i = 1; i <= G->Nv; i++)
 	{
  		printf("%-3d->", i);
   		while (G->List[i].firstVertnex != NULL)
   		{
    			printf("%-3d---weight:%-3d->",G->List[i].firstVertnex->local,G->List[i].firstVertnex->weight);
    			G->List[i].firstVertnex = G->List[i].firstVertnex->next;
   		}
   		if (G->List[i].firstVertnex == NULL)
    		printf("!\n");
 	}
}

接下来,main函数(里面包含了邻接矩阵的打印)

# include <stdio.h>
# include <stdlib.h>
# include "Graph.h"
int main()
{
 	printf("用邻接矩阵创建无向图!\n");
 	//要建立一个有vertnex个顶点无边的图
 	int vertnex;
 	printf("请输入有图中有多少个顶点:\n");
 	scanf("%d", &vertnex);
 	//建立一个图,首先要声明一个图
 	MGraph G;
 	G = (MGraph)malloc(sizeof(graph));
 	//接着要初始化图
 	InitGraph(G, vertnex);
 	//接着往图中插入数据
 	printf("请输入边数:\n");
 	int Ne;
 	scanf("%d", &Ne);
 	if (Ne != 0)
 	{ 
  		printf("请输入顶点和权重(空格分隔!)\n");
  		for (int i = 0; i < Ne; i++)
  		{
   			Edge E;
   			E = (Edge)malloc(sizeof(edge));
   			scanf("%d %d %d",&E->V1,&E->V2,&E->Weight );
   			//接下来把这些数据放入图中和边中
   			InsertEdge(G, E);
  		}
  		printf("请输入顶点的数据:\n");
  		for (int i = 0; i < vertnex; i++)
   			scanf("%d", &G->Data[i]);
 	}
 	printf("------------------------------------------------\n");
 	printf("将打印出邻接矩阵!\n\n");
 	for (int j = 0; j < vertnex; j++)
 	{
  		for (int i = 0; i < vertnex; i++)
			printf("%5d", G->Weight[i][j]);
		printf("\n");
 	}
 	printf("将打印顶点元素中的数据!\n");
 	for (int i = 0; i < vertnex; i++)
  	printf("%3d", G->Data[i]);
 	printf("-------------------------------------------------\n");
 	printf("用邻接表创建无向图\n");
 	AdjMGraph *Graph = (Adjgraph*)malloc(sizeof(Adjgraph));
 	InitAdjGraph(Graph);
 	CreateAdjGraph(Graph);
 	PrintAdjGraph(Graph);
 	system("pause");
 	return 0;
}

代码运行截图:
在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构-图的创建(邻接矩阵,邻接表)C语言实现 的相关文章

随机推荐

  • LeetCode Week 4

    LeetCode Week 4 练腿是最虐的项目 没有之一 问题集合 1 Reverse Words in a String III Easy 557 Given a string you need to reverse the order
  • 如何高效安装MindSpore的GPU版本

    作者 王磊 更多精彩分享 欢迎访问和关注 https www zhihu com people wldandan MindSpore的GPU版本以前的安装指南 只写清楚了安装依赖 但没有明确指出安装具体执行的命令 缺乏实操性 比较依赖开发者
  • 整流七 - 三相PWM整流器—公式推导篇

    此篇文章为了进一步理解三相pwm整流器 前期的四象限产品 以及仿真模型都没有彻底理解三相pwm整流器的数学模型 于是现在开始一步步推到整流器各个环节的数学公式 三相PWM整流器拓扑结构 补充 三相 VSR 中 交流侧电感的设计尤为重要 起到
  • 面积积分_A-level数学:必考题型之积分求面积解题技巧汇总!!!

    对于A level 数学的pure Math考试部分 总有那么一道积分求面积的题 很多同学在做这种题的时候总是觉得即使自己充分调动学过的所有公式都无法做出来 导致失分 那么今天潘老师带大家一起总结一下 1积分的物理意义 我们知道积分其实是微
  • SAR成像系列:【15】合成孔径雷达(SAR)运动补偿

    不同于光学图像 SAR图像的获取的本质是方位信号的累积结果 也就是说是在合成孔径时间内的电磁波照射结果 类似于光学成像中的延时拍照 通常使用手机拍照时 若拍照的手臂出现抖动 那么得到的照片就会变模糊 同样的现象也会出现在SAR图像中 SAR
  • 如何写监听回调(事件完成监听、点击响应监听 )

    转载请注明出处 如何写监听回调 事件完成监听 点击响应监听 Mr Leixiansheng的博客 CSDN博客 主要对监听回调做一个简单说明 监听的作用 某一事件只要出现 就会调用其对应的方法 进行响应操作 方式有2 1 常规方式 和控件设
  • 对虚拟机原有磁盘扩容

    对虚拟机原有磁盘扩容 扩容不会导致数据丢失 1 先关闭虚拟机 手动去虚拟机的硬盘扩容 2 lsblk df h查看磁盘容量 3 fdisk dev sda命令扩展 输入P查看分区的start和end的值 需要先删除要扩容的分区 输入d 选择
  • 转:使用DOS命令chcp查看windows操作系统的默认编码以及编码和语言的对应关系

    代码页是字符集编码的别名 也有人称 内码表 早期 代码页是IBM称呼电脑BIOS本身支持的字符集编码的名称 当时通用的操作系统都是命令行界面系统 这些操作系统直接使用BIOS供应的VGA功能来显示字符 操作系统的编码支持也就依靠BIOS的编
  • 微信小程序绘制二维码

    一 前言 在日常的小程序项目中 会经常遇到需要动态绘制二维码的需求 使用场景很多 例如绘制在海报上 例如制作票务码 核销码等等 这篇文章是应一位好友的需求而写的 也希望能够给有需要的同学一些帮助 二 实现原理 使用微信小程序的canvas组
  • STM32的低功耗模式

    目前的低功耗设计主要从芯片设计和系统设计两个方面考虑 随着半导体工艺的飞速发展和芯片工作频率的提高 芯片的功耗迅速增加 而功耗增加又将导致芯片发热量的增大和可靠性的下降 因此 功耗已经成为深亚微米集成电路设计中的一个重要考虑因素 为了使产品
  • HTML、CSS制作小米商城网页首页源码解析

    简介 这是我学习前端以来仿写的第一个项目 沿着尚硅谷李立超老师的教学视频学习 在仿写这个项目的过程中即巩固了这两周以来的知识 也增加了一些小经验 主要是老师传授 同时也让自己更加有信心学习下去 相信自己一定会实现自己的小梦想 加油 小米官网
  • [运算放大器系列]二、电压转4 - 20MA电流电路分析

    运算放大器系列 二 电压转4 20MA电流电路分析 1 电路原理图 2 原理分析 1 电路原理图 偶然在网上看到一个4 20MA转换电路原理图如下 2 原理分析 R L R L RL 为负载 分析电流流向如上图箭头所示可以得到 假设Rloo
  • Elasticsearch 6.1 TransportClient实现多条件重排序搜索查询之FilterFunctionBuilder和FunctionScoreQueryBuilder

    搜索条件 在Index为10000下查找标题包含 IPhone 优先取 品牌手机 这个分类 销量越高越前 结果随机给用户展示 JAVA 代码实现片段 String searchContent IPhone TransportClient c
  • UI测试和接口测试

    安全测试是我下个阶段的主学习了 UI测试和接口测试 安全和性能调优 和测试相关的一些专业术语 测试的发展方向大体是4种 接口自动化测试 UI自动化测试 持续集成 和测试相关的一些专业术语 QA quality assurance 质量保证
  • 调试远程tomcat服务器

    1 关闭linux下防火墙 不然远程客户机可能无法连接上该tomcat 注意不直接关闭防火墙 而是将远程客户机与端口添加到防火墙上 关闭主要是最简单 service iptables stop 2 启动tomcat 命令行下运行 catal
  • ML Impossible and Rescure

    No Rule to Define will cause conflict Using available data to estimate target function if without rule target is unknown
  • PageHelper的概述和基本使用

    PageHelper介绍 PageHelper是国内非常优秀的一款开源的mybatis分页插件 它支持基本主流与常用的数据库 例如mysql oracle mariaDB DB2 SQLite Hsqldb等 本项目在 github 的项目
  • 线与逻辑详解

    什么是线与逻辑 需要和CMOS漏极开路门 Open Drain OD 一起介绍 通常CMOS门电路都有反相器作为输出缓冲电路 而在工程实践中 有时需要将两个门的输出端并联以实现 与 逻辑的功能称为 线与 逻辑 或者用于驱动大电流负载 或者实
  • 第一章 webpack与构建发展简史

    官方loader和插件 Loaders webpack Plugins webpack 为什么需要构建工具 初识webpack webpack默认配置文件 webpack config js 可以通过webpack config
  • 数据结构-图的创建(邻接矩阵,邻接表)C语言实现

    图的定义 图 Graph G由两个集合V和E组成 记为 G V E 其中V是顶点的有穷非空集合 其实就是顶点 E是V中顶点偶对的有穷集合 就是边 V G 和E G 通常分别表示图G的顶点集合以及边集合 E G 可以为空集合 但是此时的图只有