当前位置: 首页 > news >正文

数据结构——图的基本操作

文章目录

  • 1.图
  • 2.图的结构体定义
  • 3.图的初始化
  • 4.添加顶点、删除顶点
    • 4.1添加顶点
    • 4.2删除顶点
  • 5.添加边、删除边
    • 5.1添加边
    • 5.2删除边
  • 6.打印图
  • 7.main函数

在生命旅途中,我们就像是一个个节点,被无数看不见的边相连。每一次的相识与相离,都在这张巨大的网络图中留下独特的印记。

1.图

图(graph)是一种非线性数据结构,由顶点(vertex)和边(edge)组成。我们可以将图 G 抽象地表示为一组顶点 V 和一组边 E 的集合。

在这里插入图片描述

接下来,我将使用C语言基于邻接矩阵来构建这张图

2.图的结构体定义

邻接矩阵是一种用于表示图中顶点之间关系的二维数组。在您提供的结构体定义中,GraphAdjMat 包含了三个成员:verticesadjMatsize。以下是如何使用这个结构体来表示图的简要说明:

  1. 顶点数组 vertices

    • 这是一个一维数组,用于存储图中的顶点信息。每个元素对应一个顶点,可以存储顶点的标识符或其他相关信息。
  2. 邻接矩阵 adjMat

    • 这是一个二维数组,用于表示图中顶点之间的连接关系。adjMat[i][j] 表示顶点 i 和顶点 j 之间的边的信息。通常,如果顶点 i 和顶点 j 之间有边,则 adjMat[i][j] 会被设置为1(或边的权重,如果是加权图的话),如果没有边,则设置为0。
  3. 顶点数量 size

    • 这是一个整数,表示图中当前的顶点数量。这个值用于在操作图时,知道邻接矩阵中哪些行和列是有效的。
/*  * @brief 使用邻接矩阵来实现图  * @Max_Size 图中最大顶点数量  * @param vertices 顶点数组  * @param adjMat 邻接矩阵  * @param size 图中顶点的数量  */
# define  Max_Size 255  
typedef struct  
{  int vertices[Max_Size];  int adjMat[Max_Size][Max_Size];  int size;  
}GraphAdjMat;

3.图的初始化

函数 CreateGraph 的功能是创建并初始化一个图结构,使用邻接矩阵来表示这个图。具体来说,这个函数执行以下操作:

  1. 动态分配内存:为 GraphAdjMat 结构体分配一块内存空间。
  2. 检查内存分配:如果内存分配失败,则返回 NULL
  3. 初始化邻接矩阵:将邻接矩阵中的所有元素设置为0,表示图中没有边。
  4. 设置顶点数量:将图中的顶点数量 size 设置为0,表示图是空的。
  5. 返回图结构:返回一个指向新创建并初始化的图结构的指针。

简而言之,CreateGraph 函数用于创建一个空的图,并准备好用于后续添加顶点和边的操作。

/*  * @brief 图的初始化  * @param 无  * @return 初始化后的图  */GraphAdjMat *CreateGraph()  
{  //为图创建存储空间  GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat));  //如果内存分配失败,返回空值  if (graph == NULL)  {  return NULL;  }  //将邻接矩阵初始化为0  for (int i = 0; i < Max_Size; i++)  {  for (int j = 0; j < Max_Size; j++)  {  graph->adjMat[i][j] = 0;  }  }  //将图顶点数置0  graph->size = 0;  return graph;  
}

4.添加顶点、删除顶点

4.1添加顶点

函数 addVertice 的功能是向图结构中添加一个新的顶点。以下是这个函数的简要说明:

  1. 参数检查:函数首先检查图中是否还有空间添加新的顶点。如果 graph->size 已经等于 Max_Size,即图中顶点数已达到最大值,函数将打印一条消息并返回,不执行添加操作。

  2. 添加顶点:如果图中还有空间,函数将新顶点的值 val 添加到 graph->vertices 数组的当前 size 索引处,这个索引对应于图中的下一个空位置。

  3. 更新邻接矩阵:对于新添加的顶点,函数需要更新邻接矩阵。它遍历所有已存在的顶点,并设置新顶点与这些顶点之间的连接状态为0(表示没有边)。这是通过设置 graph->adjMat[n][i]graph->adjMat[i][n] 来实现的,其中 n 是新顶点的索引。

  4. 增加顶点计数:最后,函数通过增加 graph->size 的值来更新图中顶点的数量。

简而言之,addVertice 函数用于在图的 vertices 数组中添加一个新的顶点,并在邻接矩阵中为这个新顶点初始化与其他顶点之间的边的状态。

/*  * @brief 添加顶点  * @param graph 要添加顶点的图  * @param val 要添加的元素  * @return 无  */void addVertice(GraphAdjMat *graph,int val)  
{  //如果图满,退出程序  if (graph->size == Max_Size)  {  printf("图已满!\n");  return;  }  //添加第n个结点  int n = graph->size;  graph->vertices[n] = val;  //将第n个结点的行和列均置0  for (int i = 0; i < n; i++)  {  graph->adjMat[n][i] = graph->adjMat[i][n] = 0;  }  graph->size++;  }

4.2删除顶点

函数 deleteVertice 的功能是从图结构中删除指定索引位置的顶点。以下是这个函数的简要说明:

  1. 参数检查:函数首先检查传入的索引 index 是否有效。如果索引小于0或大于等于图中顶点的数量 graph->size,则表示索引越界,函数将打印一条错误消息并返回,不执行删除操作。

  2. 删除顶点:如果索引有效,函数将从 vertices 数组中删除索引为 index 的顶点。这是通过将 index 位置及其后面的所有顶点向前移动一个位置来实现的。

  3. 删除邻接矩阵中的行:对于邻接矩阵中索引为 index 的行,函数将该行及其后面的所有行向前移动一个位置,以删除对应的行。

  4. 删除邻接矩阵中的列:对于邻接矩阵中索引为 index 的列,函数将该列及其后面的所有列向前移动一个位置,以删除对应的列。

  5. 更新顶点计数:最后,函数通过减少 graph->size 的值来更新图中顶点的数量。

简而言之,deleteVertice 函数用于从图中删除指定索引的顶点,并相应地更新 vertices 数组和邻接矩阵,以保持图的一致性。

/*  * @brief 删除顶点  * @param graph 图  * @param index 删除顶点的索引  * @return 无  */void deleteVertice(GraphAdjMat *graph,int index)  
{  //处理索引越界的情况  if (index <0 || index >= graph->size)  {  printf("索引越界!\n");  return;  }  //将顶点删除  for (int i = index; i < graph->size - 1;i++)  {  graph->vertices[i] = graph->vertices[i + 1];  }  //删除行  for (int i = index; i < graph->size -1;i++)  {  for (int j = 0; j < graph->size; j++)  {  graph->adjMat[i][j] = graph->adjMat[i + 1][j];  }  }  //删除列  for (int i = 0; i < graph->size; i++)  {  for (int j = index; j < graph->size - 1;j++)  {  graph->adjMat[i][j] = graph->adjMat[i][j + 1];  }  }  //顶点个数减1  graph->size--;  
}

5.添加边、删除边

5.1添加边

函数 addEdge 的功能是向图中添加一条边,连接两个指定索引的顶点。以下是这个函数的简要说明:

  1. 参数检查:函数首先检查传入的两个索引 index1index2 是否有效。如果任一索引小于0或大于等于图中顶点的数量 graph->size,则表示索引非法,函数将打印一条错误消息并返回,不执行添加边的操作。

  2. 添加边:如果两个索引都有效,函数将在邻接矩阵中设置 index1index2 之间的连接状态。具体来说,它将 graph->adjMat[index1][index2]graph->adjMat[index2][index1] 都设置为1。这表示在顶点 index1 和顶点 index2 之间添加了一条双向连接的边。

简而言之,addEdge 函数用于在图中添加一条无向边,连接两个指定索引的顶点,并在邻接矩阵中相应地更新这两个顶点之间的连接状态。

/*  * @brief 添加边  * @param graph * @param index1 index2 要添加的两个边在vertics中的索引  * @return 无  */void addEdge(GraphAdjMat *graph,int index1,int index2)  
{  if (index1 < 0 || index1 >= graph->size || index2 < 0 || index2 >= graph->size)  {  printf("索引非法\n");  return;  }  //添加边  graph->adjMat[index1][index2] = 1;  graph->adjMat[index2][index1] = 1;  }

5.2删除边

函数 deleteEdge 的功能是从图中删除连接两个指定索引顶点之间的边。以下是这个函数的简要说明:

  1. 参数检查:函数首先检查传入的两个索引 index1index2 是否有效。如果任一索引小于0或大于等于图中顶点的数量 graph->size,则表示索引非法,函数将打印一条错误消息并返回,不执行删除边的操作。

  2. 删除边:如果两个索引都有效,函数将在邻接矩阵中将 index1index2 之间的连接状态设置为0。具体来说,它将 graph->adjMat[index1][index2]graph->adjMat[index2][index1] 都设置为0。这表示在顶点 index1 和顶点 index2 之间删除了一条双向连接的边。

简而言之,deleteEdge 函数用于在图中删除一条无向边,该边连接两个指定索引的顶点,并在邻接矩阵中相应地更新这两个顶点之间的连接状态。

/*  * @brief 删除边  * @param graph 图  * @param index1,index2 要删除的两个顶点之间的边在vertices中的索引  */void deleteEdge(GraphAdjMat *graph, int index1, int index2)  
{  if (index1 < 0 || index1 >= graph->size || index2 < 0 || index2 >= graph->size)  {  printf("索引非法\n");  return;  }  //添加边  graph->adjMat[index1][index2] = 0;  graph->adjMat[index2][index1] = 0;  
}

6.打印图

/*  * @brief 打印邻接矩阵  * @param graph 图  * @return 无  */void printGraph(GraphAdjMat *graph)  
{  //输出顶点  for (int i = 0; i < graph->size; i++)  {  printf("%d ",graph->vertices[i]);  }  printf("\n");  //打印边  for (int i = 0; i < graph->size; i++)  {  for (int j = 0; j < graph->size; j++)  {  printf("%d ",graph->adjMat[i][j]);  }  printf("\n");  }  
}

7.main函数

int main(void)  
{  GraphAdjMat *graph = CreateGraph();  //添加顶点  int arr[] = {1,2,3,4,5,6,7,8,9};  for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)  {  addVertice(graph,arr[i]);  }  //添加边  /*  * 1 2 3     * 4 5 6     * 7 8 9     */    addEdge(graph,0,1);  addEdge(graph,1,2);  addEdge(graph,0,3);  addEdge(graph,1,4);  addEdge(graph,2,5);  addEdge(graph,3,4);  addEdge(graph,4,5);  addEdge(graph,3,6);  addEdge(graph,4,7);  addEdge(graph,5,8);  addEdge(graph,6,7);  addEdge(graph,7,8);  //打印图  printGraph(graph);  return 0;  
}

输出:

D:\develop\Ccode\Data_Structure_Learn\Map\cmake-build-debug\Map.exe
1 2 3 4 5 6 7 8 9
0 1 0 1 0 0 0 0 0
1 0 1 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0
1 0 0 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0
0 0 0 0 1 0 1 0 1
0 0 0 0 0 1 0 1 0Process finished with exit code 0

相关文章:

数据结构——图的基本操作

文章目录 1.图2.图的结构体定义3.图的初始化4.添加顶点、删除顶点4.1添加顶点4.2删除顶点 5.添加边、删除边5.1添加边5.2删除边 6.打印图7.main函数 在生命旅途中&#xff0c;我们就像是一个个节点&#xff0c;被无数看不见的边相连。每一次的相识与相离&#xff0c;都在这张巨…...

掌握全球速递:在表格中高效利用国际快递公式查询快递

在当今全球化的商业环境中&#xff0c;国际快递服务已成为连接世界各地企业与个人的重要桥梁。无论是跨国企业间的货物运输&#xff0c;还是个人用户的海外购物需求&#xff0c;国际快递都扮演着不可或缺的角色。然而如何快速准确地获取大量国际快递的物流轨迹成为了一个挑战。…...

【MySQL系列】字符集设置

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

Vue2进阶之Vue3高级用法

Vue3高级用法 响应式Vue2&#xff1a;Object.definePropertyObject.definePropertythis.$set设置响应式 Vue3&#xff1a;Proxy composition APIVue2 option API和Vue3 compositionAPIreactive和shallowReactivereadonly效果toRefs效果 生命周期main.jsindex.htmlLifeCycle.vue…...

基于微信的追星小程序+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;追星小程序被用户普遍使用&#xff0c;为方便用户能够可以…...

【51单片机】串口通信原理 + 使用

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 串口硬件电路UART串口相关寄存器 编码单片机通过串口发送数据电脑通过串口发送数据控制LED灯 串口 串口是一种应用十分广泛的通讯接…...

优选算法第五讲:位运算模块

优选算法第五讲&#xff1a;位运算模块 1.常见的位运算总结2.判断字符是否唯一3.丢失的数字4.两整数之和5.只出现一次的数字II6.消失的两个数字 1.常见的位运算总结 2.判断字符是否唯一 链接: link class Solution { public:bool isUnique(string astr) {if(astr.size() >…...

【07】Maven项目多环境打包配置

&#xff08;1&#xff09;Web项目使用Maven进行多模块划分开发之后&#xff0c;面临一个问题&#xff0c;即如何加载不同环境的配置文件打包发布到不同的环境中&#xff1f; &#xff08;2&#xff09;不同的环境有开发环境、测试环境、线上生产环境等。 &#xff08;3&#x…...

嵌入式Linux入门具备:C语言基础与基本驱动学习(2):Linux GIibc IO基础

标准IO 标准 I/O 虽然是对文件 I/O 进行了封装&#xff0c;但事实上并不仅仅只是如此&#xff0c;标准 I/O 会处理很多细节&#xff0c;譬如分配 stdio 缓冲区、以优化的块长度执行 I/O 等&#xff0c;这些处理使用户不必担心如何选择使用正确的块长度。I/O 库函数是构建于文件…...

【微服务】Docker 容器化

一、初识Docker 1. 为什么需要 Docker 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会遇到一些问题&#xff1a; 依赖关系复杂&#xff0c;容易出现兼容性的问题开发、测试、生产环境有差异 Docker 如何解决依赖的兼容问题 将应用的Libs&#xff08;…...

[前端] 为网站侧边栏添加搜索引擎模块

前言 最近想给我的个人网站侧边栏添加一个搜索引擎模块&#xff0c;可以引导用户帮助本站SEO优化&#xff08;让用户可以通过点击搜索按钮完成一次对本人网站的搜索&#xff0c;从而实现对网站的搜索引擎优化&#xff09;。 最开始&#xff0c;我只是想实现一个简单的百度搜索…...

解决CORS (跨源资源共享) 错误

问题引入 前端代码 <template><div id"hello-vue" class"demo">{{ message }}</div><el-button type"primary" click"handleClick">我是一个按钮</el-button></template><script setup>//加…...

Redis 实现分布式缓存

一、引言 在当今互联网时代&#xff0c;随着业务的不断发展和用户量的持续增长&#xff0c;系统的性能和可扩展性成为了关键挑战。分布式缓存作为一种重要的技术手段&#xff0c;能够有效地缓解数据库压力、提高系统响应速度、增强系统的可扩展性。Redis 作为一种高性能的内存数…...

Chrome与火狐哪个浏览器的移动版本更流畅

在当今的数字化时代&#xff0c;移动设备已经成为我们生活中不可或缺的一部分。而浏览器作为我们访问互联网的重要工具&#xff0c;其性能和用户体验直接影响到我们的使用感受。本文将对比Chrome和火狐&#xff08;Firefox&#xff09;两款主流浏览器的移动版本&#xff0c;探讨…...

7篇Python爬虫实例,直接代码可运行,全网最全,注释超详细(适合收藏)——2、爬取图片信息。

7篇Python爬虫实例&#xff0c;可直接运行&#xff0c;适合收藏 python爬虫7篇实例&#xff0c;分七个文章进行发布&#xff1b;第二篇&#xff1a;爬取图片信息。 爬取图片信息&#xff0c;并将每张图片都下载下来。 爬虫主要三部分&#xff1a; 1、获取数据 2、数据解析 3、…...

25.停车场管理系统(基于web的Java项目)

目录 1.系统的受众说明 2.相关技术与方法 3.系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 需求分析 3.2.1 系统功能描述 3.2.2 用例图分析 4. 系统设计 4.1 系统类分析 5. 系统详细设计与实现 5.1 用户登录 5.2 系统信…...

展览搭建公司怎么跟展会主办打好交道

与展会主办打好交道的重要性 首先&#xff0c;我们得明白&#xff0c;展览搭建公司为何要跟展会主办打交道。简单地说&#xff0c;展会主办拥有大量的参展商信息。这些参展商是展览搭建公司潜在的客户群体&#xff0c;与主办打好交道&#xff0c;就等于拿到了通向这些客户的 “…...

软件开发方法

软件开发方法是一种用于指导软件开发过程的系统性方法,它涵盖了从需求分析、设计、编码、测试到维护的整个软件生命周期。软件开发方法通常包括一系列的步骤、技术和工具,以确保软件的质量、可维护性和可扩展性。 常见的软件开发方法有瀑布模型、敏捷开发、螺旋模型等。这些…...

「Mac畅玩鸿蒙与硬件24」UI互动应用篇1 - 灯光控制小项目

本篇将带领你实现一个互动性十足的灯光控制小项目&#xff0c;用户可以通过点击按钮来控制灯光的开关。该项目将涉及状态管理、动态图片加载以及按钮交互&#xff0c;是学习鸿蒙应用开发的重要基础。 关键词 UI互动应用状态管理动态图片加载用户交互 一、功能说明 在这个灯光…...

十二:java web(4)-- Spring核心基础

目录 创建项目 Spring 核心基础 Spring 容器 Spring 容器的作用 Spring 容器的工作流程 Bean Bean 的生命周期 IOC&#xff08;控制反转&#xff09;与依赖注入&#xff08;DI&#xff09; 控制反转的概念 依赖注入的几种方式&#xff08;构造器注入、Setter 注入、接…...

嵌入式Linux UVC驱动开发:DWC2控制器与处理单元数据流详解

1. 项目概述&#xff1a;从DWC2控制器到UVC处理单元在嵌入式Linux系统里搞USB摄像头驱动开发&#xff0c;尤其是用DWC2这种集成在SoC里的USB控制器&#xff0c;UVC&#xff08;USB Video Class&#xff09;驱动的“处理单元”绝对是个绕不开的核心。很多朋友在移植或调试摄像头…...

Ventoy主题定制完全指南:让你的启动界面焕然一新!

Ventoy主题定制完全指南&#xff1a;让你的启动界面焕然一新&#xff01; 【免费下载链接】Ventoy A new bootable USB solution. 项目地址: https://gitcode.com/GitHub_Trending/ve/Ventoy 还在使用单调乏味的启动界面吗&#xff1f;Ventoy作为一款革命性的可启动U盘解…...

【Feed 高并发架构实战】:雪花 ID + 三级缓存 + 计数旁路设计详解

&#x1f525;你好我是fengxin_rou这是我的个人主页fengxin_rou的主页 ❄️欢迎查看我的专栏我的专栏 《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWebAI的talis学习系统》、《苍穹外卖》 目录…...

注意力的几何本质:一个空间与两个算子的统一框架

1. 项目概述&#xff1a;这不是又一篇讲Attention机制的“科普文”如果你最近翻过几篇顶会论文&#xff0c;或者在GitHub上扫过几个热门Transformer库的源码&#xff0c;大概率会在某个角落撞见“The Geometry of Attention: One Space, Two Operators”这个标题。它不像“Atte…...

Maven依赖scope:从编译到打包,一张图理清生命周期与classpath

Maven依赖scope全解析&#xff1a;构建生命周期与classpath的精准控制 当你盯着pom.xml里那些<scope>compile</scope>标签时&#xff0c;是否曾好奇它们究竟如何影响你的构建流程&#xff1f;Maven的依赖scope就像一个个精密的开关&#xff0c;控制着依赖项在编译、…...

还在熬夜起草各类通知?2026便捷AI办公好物,轻松写完正式公文

作为一名在行政岗摸爬滚打五年的职场人&#xff0c;我每天的工作不是泡在各类会议里&#xff0c;就是埋头起草通知、整理纪要。相信不少行政、文秘岗位的朋友都和我有一样的困扰&#xff1a;公司部门多、会议密&#xff0c;每周光是例会、项目协调会、临时部署会就要开三四场&a…...

【工具全景】2025全球AI自动化测试工具矩阵库(商业化 vs 开源项目梳理)

前言:测试工程师正在被AI重新定义 2025年,测试领域正在经历一场前所未有的变革。据MarketsandMarkets最新报告显示,全球AI测试自动化市场规模在2025年达到88.1亿美元,预计到2032年将飙升至359.6亿美元,年复合增长率高达22.3%。与此同时,Gartner在2025年10月首次发布了《…...

NLP之BERT预训练模型详解

摘要&#xff1a; BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是谷歌于2018年提出的革命性自然语言处理模型&#xff0c;首次将基于Transformer的双向编码器架构成功应用于预训练语言模型&#xff0c;在多项NLP基准任务上刷新了最优…...

ISTA 3B-2013 全解析|零担货物 (LTL) 综合模拟运输测试标准(CSDN 完整版)前言

前言 ISTA 3B-2013 是 ISTA 3 系列高级综合模拟测试&#xff0c;专门针对零担货物运输&#xff08;LTL&#xff09; 的包装件。 零担运输的特点是多货混装、多次中转、人工 / 叉车交叉搬运、环境复杂&#xff0c;因此 3B 是工业、设备、家电、汽配、大型包装最贴近真实物流的测…...

实战测试10款降AIGC软件:只选真正管用的那一款!

随着AI写作工具的普及&#xff0c;论文撰写和内容创作变得前所未有的高效&#xff0c;许多学生和职场人都从中受益。然而&#xff0c;随着AIGC检测技术的不断升级&#xff0c;越来越多的人开始面临新的挑战&#xff1a;原本流畅自然的AI生成内容&#xff0c;如今很容易被系统识…...