【数据结构】七、图

一、概念

:记为G(V,E)

有向图:每条边都有方向

无向图:边无方向

完全图:每个顶点都与剩下的所有顶点相连

完全有向图有n(n-1)条边;完全无向图有n(n-1)/2条边

带权图:边上标有数值的图

连通图:任意两点都有路可走

要连通具有n个顶点的有向图,至少需要n条边。(构成环)

生成树:该树包含图的所有n个节点,树有n-1条边,将图连通。

如果添加一条边,必定出现环;

邻接点:一条边两端的点互为邻接点。

:与点相连的边的条数为点的度;有向图中,从点发出的边数叫点的出度 ,在点终止的边数叫点的入度。

路径:从一个点沿着边走到另一个点,途径的顶点序列叫路径。

路径长度:非带权图的路径长度是指此路径上边的条数; 带权图的路径长度是指路径上各边的权之和。

二、图的存储结构

2.1邻接矩阵

建立一个长和宽都为顶点数的数组,用数组元素的值表示点之间的连接情况。

无向图:

  • 点不相连为0,相连为1
  • 无向图邻接矩阵对称
  • 顶点i的度 = 第i行/列中,1的个数

有向图

  • 不对称
  • 元素a_{ij}为1,表明有一条点i指向点j的边(注意方向)
  • 顶点i的出度 = 第i行中,1的个数
  • 顶点i的入度 = 第i列中,1的个数
  • 顶点i的度 = 第i行和第i列中,1的个数之和

带权图

相当于把有向图的1换为边上的值

设图的邻接矩阵如下所示。各顶点的度依次是(   )

A. 1 2 1 2

B. 2 2 1 1

C. 3 4 2 3

D. 4 4 2 2

答案:C   行和列的1求和

空间复杂度:n^2

代码

int* adjacent_mat(int v, int e, int direct) 
{int* mat = (int*)malloc(sizeof(int) * v * v);  //分配空间if (!mat) return NULL;int i, start, end, weight;for (i = 0; i < v * v; i++)mat[i] = INF;           //节点全部初始化为无穷大(设无穷为65535)for (i = 0; i < e; i++)     //输入边信息{printf("输入起始 终止 权值:");scanf_s("%d %d %d", &start, &end, &weight);direct == 0 ? mat[start * v + end] = mat[end * v + start] = weight : mat[start * v + end] = weight;      //direct=0时生成无向图,对称赋值;等于1生成有向图}for (i = 0; i < v * v; i++) {mat[i] == INF ? printf("0 ") : printf("%d ", mat[i]);if ((i + 1) % v == 0)printf("\n");}return mat;
}

2.2邻接表

适用于稀疏矩阵

对每个节点建立一个链表,把与之相连的点存入节点,连接起来

每个节点的链表的结构

无向图的邻接表,顶点的度=该节点链表子结点个数:

有向图的邻接表和逆邻接表:

空间复杂度(n+e)  点数加边数

代码

node* adjacency_list(int v, int e, int direct) 
{headnode* headlist = (headnode*)malloc(sizeof(headnode) * v);if (!headlist) return NULL;int i;for (i = 0; i < v; i++) {headlist[i].data = i;headlist[i].next = NULL;}int start, end, weight;node* p, * newnode;for (i = 0; i < e; i++){printf("输入起始 终止 权值:");scanf_s("%d %d %d", &start, &end, &weight);if (direct != 2) {newnode = (node*)malloc(sizeof(node));newnode->adjvex = newnode->data = end;newnode->next = NULL;p = headlist[start].next;if (!p) {headlist[start].next = newnode;}else {while ((p->next) != NULL)p = p->next;p->next = newnode;}}if (direct == 0 || direct == 2){newnode = (node*)malloc(sizeof(node));newnode->adjvex = newnode->data = start;newnode->next = NULL;p = headlist[end].next;if (!p) {headlist[end].next = newnode;}else {while ((p->next) != NULL)p = p->next;p->next = newnode;}}}for (i = 0; i < v; i++) {printf("%d:", headlist[i].data);p = headlist[i].next;while (p) {printf("%d ", p->data);p = p->next;}printf("\n");}
}

2.3十字链表

考试没要求,先不写

三、图的遍历

3.1深度优先DFS

沿着一条路一直走,没路了再回头找最近的分岔路口

如上图,从v1开始DFS,可得到v1-v2-v4-v8-v5-v3-v6-v7

void DFS(int*mat, int v, int num, int*& visited)  //深度优先,递归版。需要传入邻接矩阵,点数,起始点编号,已访问数组
{printf("%d", num);             //输出起始节点visited[num] = 1;              //标记起始节点for (int i = 0; i < v; i++)    //在起始节点这一行从头搜索未遍历过的邻接点,进行递归if (!visited[i] && mat[num * v + i] != INF)DFS(mat, v, i, visited);	
}void DFS2(int* mat, int v) 
{int head = 0, i;int* stack = (int*)malloc(sizeof(int) * v);     //创建栈if (!stack) return;int* visited = (int*)malloc(sizeof(int) * v);   //创建标记数组,初始化为0if (!visited)  return;for (i = 0; i < v; i++)visited[i] = 0;int num;printf("输入开始节点编号:");scanf_s("%d", &num);//循环中,入栈在最后,所以先入栈一个点stack[head++] = num;      //入栈,标记, 输出visited[num] = 1;printf("%d", num);int temp;while (head)    //栈空时结束{temp = stack[head - 1];   //获取栈顶for (i = 0; i < v; i++)   //对栈顶对应的邻接矩阵所在行进行遍历,有未标记且相邻的就赋值退出,找不到则退栈{if (visited[i] == 0 && mat[temp * v + i] != INF) {temp = i;break;}}if (i == v)   //找不到退栈,找到入栈head--;else {stack[head++] = i;visited[i] = 1;printf("%d", i);}}
}void DFS3(int* mat, int v) 
{int top = -1, i;int* stack = (int*)malloc(sizeof(int) * v);     //创建栈if (!stack) return;int* visited = (int*)malloc(sizeof(int) * v);   //创建标记数组,初始化为0if (!visited)  return;for (i = 0; i < v; i++)    //标记数组初始化为0visited[i] = 0;int num;printf("输入开始节点编号:");scanf_s("%d", &num);//循环中,入栈在最后,所以先入栈一个点stack[++top] = num;      //入栈,top指向栈顶元素visited[num] = 1;        //标记int temp;while (top >= 0)    //栈空时结束{temp = stack[top];   //获取栈顶printf("%d", temp);for (i = 0; i < v; i++)   //对栈顶对应的邻接矩阵所在行进行遍历,有未标记且相邻的就赋值退出,找不到则退栈{if (visited[i] == 0 && mat[temp * v + i] != INF) {stack[++top] = i;visited[i] = 1;break;}}if (i == v)   //该节点所有相邻节点访问完毕,退栈top--;}
}

3.2广度优先BFS

访问节点,把它的子节点全部访问,再依次访问子节点的全部子节点

四、最小生成树

生成树:是一个极小连通子图,它含有图中全部顶点,但只有n-1条边

最小生成树:各边权值之和最小的树

PRIM普利姆算法

将所有点分为树U和图V两个集合,找到UV两个点集之间的权值最小边,把该边V中的点移到U里面,重复,直到V点全部到U里面

#include<stdio.h>
#include<stdlib.h>#define INF 65535typedef struct node {int adjvex;int data;struct node* next;
}node;typedef struct {int data;node* next;
}headnode;int* adjacent_mat(int v, int e, int direct)
{int* mat = (int*)malloc(sizeof(int) * v * v);if (!mat) return NULL;int i, start, end, weight;for (i = 0; i < v * v; i++)mat[i] = INF;for (i = 0; i < e; i++){printf("输入起始 终止 权值:");scanf_s("%d %d %d", &start, &end, &weight);direct == 0 ? mat[start * v + end] = mat[end * v + start] = weight : mat[start * v + end] = weight;}for (i = 0; i < v * v; i++) {mat[i] == INF ? printf("0 ") : printf("%d ", mat[i]);if ((i + 1) % v == 0)printf("\n");}return mat;
}void prim(int* mat, int v)   //求最小生成树。始终寻找未标记点到已标记集合的最短距离;而迪杰斯特拉是寻找未标记点到源点的最短距离
{int i, num, min, x, time = 0;int* visited = (int*)malloc(sizeof(int) * v);   //标记是否已遍历if (!visited) return;for (i = 0; i < v; i++)visited[i] = 0;int* lowcost = (int*)malloc(sizeof(int) * v);   //lowcost为未标记点到已标记点集合的最小值,from为与之连接的点的编号if (!lowcost) return;int* from = (int*)malloc(sizeof(int) * v);if (!from) return;printf("输入起始节点:");scanf_s("%d", &num);visited[num] = 1;time++;for (i = 0; i < v; i++) {lowcost[i] = mat[num * v + i];from[i] = num;}while (time != v){min = INF;for (i = 0; i < v; i++)    //找到距离最小点,传播{if (lowcost[i] < min && visited[i] != 1){min = lowcost[i];x = i;}}printf("%d-%d\n", from[x], x);visited[x] = 1;//num = x;time++;for (i = 0; i < v; i++)   //更新距离最小值if (mat[x * v + i] < lowcost[i] && visited[i] != 1) {lowcost[i] = mat[x * v + i];from[i] = x;}}
}int main() {int v, e, direct;printf("输入点数 边数:");scanf_s("%d %d", &v, &e);printf("无向图输入0,有向图输入1:");scanf_s("%d", &direct);int* admat = adjacent_mat(v, e, direct);prim(admat, v);
}

时间复杂度O(n^2)

五、拓扑排序

1.选定一个没有直接前驱的点,输出

2.删除该起点和它相邻的边

3.重复1,2直到点全部输出,得到拓扑序列

如果还有未输出的节点,说明这些点都有直接前驱,即图中存在环

拓扑排序可以判断图是否有环

拓扑序列为:4 0 3 2 1 5

对上图进行拓补排序,可以得到不同的拓扑序列的个数是(    )

答案:3

六、关键路径

顶点表示事件,有向边表示活动,边的权值表示完成活动所需时间,这样的网络叫AOE网

比如说,我们要完成一项工程,为了达到最终目标,我们要先完成许多小任务

事件最早发生时间VE:是到这一事件的最长路径。因为只有所有前期工作都做完了,这个事件才能发生,所以把前期工作完成需要的最长时间是该事件的最早发生时间。

  1. ve(源点)=0
  2. ve(k) = Max{ ve{j} + Weight(j, k) }, j为k的任意前提顶点, Weight(j, k)表示<j, k>边上的权值 

事件的最晚发生时间VL:在完成总工程所需时间不变的情况下,一个事件最晚可以发生的时间。比如ab是c事件的前期工作,a花的时间比b长,由于c最早开始时间由最长的路径决定,所以b可以推迟一会再发生,也不会耽误总工期。

  1. vl(汇点) = ve(汇点)
  2. vl(k) = Min{ vl(j) - Weight(k, j) } ,k为j的任意前驱

边的最早开始时间E:即起始点的最早发生时间,事件一发生,活动就开始

边的最晚开始时间L:终点的最晚发生时间减去活动所需时间,由活动结束最晚时间倒退活动最晚开始时间

时间余量:活动的最早最晚开始时间之差,代表该活动最长拖延时间。如果该活动时间余量为0,说明为保证总时间,该活动不能拖延,称其为关键活动。把关键活动连起来得到关键路径

应用题模板

写出点的ve和vl,边的权值、e和l

下列关于AOE网的叙述中,不正确的是( )。
A.关键活动不按期完成就会影响整个工程的完成时间
B.任何一个关键活动提前完成,那么整个工程将会提前完成
C.所有的关键活动提前完成,那么整个工程将会提前完成
D.某些关键活动提前完成,那么整个工程将会提前完成

答案:B   关键路径是网络中最长路径,表示完成工程的最短时间。关键活动延期,总工程延期;关键活动提前,总工程不一定提前,因为关键路径可能不止一条

七、最短路径

迪杰斯特拉算法

用于求一个点到其它点的最短路径

找与起点相连的节点,更新距离和路径

找距离最小的节点,将其固定,更新为起点

应用题模板

如下有向带权图,若采用迪杰斯特拉算法求源点a到其他各顶点的最短路径,得到的第一条最短路径的目标顶点是b,第二条最短路径的目标顶点是c,后续得到的其余各最短路径的目标顶点依次是()

答案:fde

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/584067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 pytest 相关特性重构 appium_helloworld

一、前置说明 在 pytest 基础讲解 章节,介绍了 pytest 的特性和基本用法,现在我们可以使用 pytest 的一些机制,来重构 appium_helloworld 。 appium_helloworld 链接: 编写第一个APP自动化脚本 appium_helloworld ,将脚本跑起来 代码目录结构: pytest.ini 设置: [pyt…

Goby 漏洞发布| QNAP NAS authLogin.cgi 命令执行漏洞(CVE-2017-6361)

漏洞名称&#xff1a;QNAP NAS authLogin.cgi 命令执行漏洞&#xff08;CVE-2017-6361&#xff09; English Name&#xff1a;QNAP NAS authLogin.cgi command execution vulnerability (CVE-2017-6361) CVSS core: 9.8 影响资产数&#xff1a; 2637547 漏洞描述&#xff1…

pd工具箱 Parallels Toolbox

Parallels Toolbox是一款适用于Mac和PC的强大工具箱软件&#xff0c;集成了多种实用的工具和功能&#xff0c;旨在帮助用户提高工作效率和生产力。它提供了文件管理、截图、音频视频处理、清理工具等众多工具&#xff0c;使用户能够方便快捷地完成各种常见任务。Parallels Tool…

“踩坑”经验分享:Swift语言落地实践

作者 | 路涛、艳红 导读 Swift 是一种适用于iOS/macOS应用开发、服务器端的编程语言。自2014年苹果发布 Swift 语言以来&#xff0c;Swift5 实现了 ABI 稳定性、Module 稳定性和Library Evolution&#xff0c;与Objective-C&#xff08;下文简称“OC”&#xff09;相比&#xf…

PLC电机正反转控制程序示例

一、对于三相电源线的电动机&#xff0c;反转只需要任意的交换两根电源线即可 二、例如接通KM1对应正转的话&#xff0c;则接通KM2则对应反转 三、电机正转按钮及其对应的地址 四、电机反转按钮及其对应的地址 五、电机停止按钮及其对应的地址 六、正转的接触器线圈 七、反转的…

在VMware上安装Ubuntu:详细教程

关于VMware和Ubuntu VMware VMware 是一家全球领先的虚拟化和云基础架构解决方案提供商。它提供了多个产品和技术&#xff0c;用于管理和优化计算机资源的使用&#xff0c;实现虚拟化、云计算和数据中心自动化等功能。 以下是 VMware 公司提供的一些主要产品&#xff1a; V…

决心解开软光栅的心结

最近几天离职在家,是的,还没回老家.白天周中的时候写这个软光栅化渲染器.包括在上班的最后项目大家都不干活的时候我已经开始写了.到今天上午总算是有的看了.细节还差很多,下午把透视校正插值加上,下午加不完就元旦假期之后再说(元旦我要写pbrt的读书笔记).还有摄像机裁剪,背面…

《掌握需求管理,助你打造火爆产品》

作为一名产品经理&#xff0c;需求管理是你工作中最重要的部分之一。一个好的需求管理系统可以帮助你确保你的产品始终符合客户的需求和期望&#xff0c;并确保项目能够按时交付。下面是一些建议&#xff0c;帮助你成为一个更好的需求管理者。 建立清晰的需求管理流程 一个好的…

一文详解Cookie以及Selenium自动获取Cookie

前言 以后数据获取途径以及数据资产绝对会是未来核心要素生产工具和资源之一&#xff0c;每个大模型都离不开更加精细化数据的二次喂养训练。不过现在来看收集大量数据的方法还是有很多途径的&#xff0c;有些垂直领域的专业数据是很难获取得到的&#xff0c;靠人力去搜寻相当…

Arduino stm32 USB CDC虚拟串口使用示例

Arduino stm32 USB CDC虚拟串口使用示例 &#x1f4cd;相关篇《STM32F401RCT6基于Arduino框架点灯程序》&#x1f516;本开发环境基于VSCode PIO&#x1f33f;验证芯片&#xff1a;STM32F401RC⌛USB CDC引脚&#xff1a; PA11、 PA12&#x1f527;platformio.ini配置信息&…

【滑动窗口】【二分查找】C++算法:和至少为 K 的最短子数组

作者推荐 动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本 本题涉及知识点 滑动窗口 有序向量 二分查找 LeetCode862:和至少为 K 的最短子数组 给你一个整数数组 nums 和一个整数 k &#xff0c;找出 nums 中和至少为 k 的 最短非空子数组 &#xff0c;并返回…

HCIA-Datacom题库(自己整理分类的)——ARP协议【完】

一、单选 1.ARP 属于哪一层协议&#xff1f; 数据链路层 网络层 物理层 传输层 2.ARP请求是____发送的 点播 广播 组播 单播 关于ARP报文的说法错误的是? ARP请求报文是广播发送的 ARP报文不能被转发到其他广播域 ARP应答报文是单播方发送的 任何链路层协议都需…

开放式蓝牙耳机学生党适合买哪些?平价好用的开放式耳机推荐

对于学生党来说&#xff0c;想要买一款既平价又好用的开放式蓝牙耳机&#xff0c;确实需要仔细挑选&#xff0c;那啥是开放式耳机呢&#xff1f;简单来说&#xff0c;开放式耳机就是那种不把耳朵全部封闭起来的耳机&#xff0c;声音可以流通&#xff0c;听起来更自然、舒适&…

LVM与磁盘配额

文章目录 LVM与磁盘配额一、LVM概述1、LVM概述2、LVM机制的基本概念2.1 PV&#xff08;Physical Volume&#xff0c;物理卷&#xff09;2.2 VG&#xff08;Logical Volume&#xff0c;逻辑卷&#xff09;2.3 LV&#xff08;Logical Volume&#xff0c;逻辑卷&#xff09; 二、L…

Android移动端超分辨率调研(未完成 目前自用)

作用 图片加载是目前几乎所有的APP都具备的基础能力&#xff0c;在节省服务商的传输带宽之外&#xff0c;也可以降低用户消费端流量的消耗&#xff0c;提升用户的加载速度。帮助每一个产品用更低的成本达到更好的图片加载效果。 效果 另一方面 用TensorFlow实现的图像极度压…

Nginx配置反向代理

代理通常用于在多个服务器之间分配负载&#xff0c;无缝显示来自不同网站的内容&#xff0c;或通过 HTTP 以外的协议将处理请求传递给应用程序服务器。 将请求传递到代理服务器 当 NGINX 代理请求时&#xff0c;它会将请求发送到指定的代理服务器&#xff0c;获取响应&#x…

大语言模型发展史

前言 2023年可谓是生成式AI元年&#xff0c;大语言模型从崭露头角到锋芒毕露&#xff0c;已然成为人工智能领域的关键推动力。这一创新性的技术不仅在自然语言处理领域崭露头角&#xff0c;更深刻地改变了我们对人机交互、智能助手和信息处理的认知。那么大语言模型的发展历程…

HarmonyOS云开发基础认证【题库答案】

HarmonyOS应用开发者基础认证【题库答案】 HarmonyOS应用开发者高级认证【题库答案】 一、判断题 应用架构的演进依次经历了微服务架构、单体架构、Serverless架构等阶段。&#xff08;错&#xff09;鸿蒙应用可以使用Arkts开发&#xff08;对&#xff09;认证服务在绑定微信账…

如何使用ArcGIS Pro自动矢量化建筑

相信你在使用ArcGIS Pro的时候已经发现了一个问题&#xff0c;那就是ArcGIS Pro没有ArcScan&#xff0c;在ArcGIS Pro中&#xff0c;Esri确实已经移除了ArcScan&#xff0c;没有了ArcScan我们如何自动矢量化地图&#xff0c;从地图中提取建筑等要素呢&#xff0c;这里为大家介绍…

基于 eBPF 构建下一代智能可观测系统

作者&#xff1a;梵登、千陆 本文基于 KubeCon China 2023 分享整理 我们今天分享的主题是基于 eBPF 构建下一代智能可观测系统。 在开始之前呢&#xff0c;我先介绍一下我们自己。我是刘恺&#xff0c;花名是千陆&#xff0c;目前是阿里云 ARMS K8s 监控子产品的负责人。这…