【数据结构】七、图

news2024/12/23 18:30:52

一、概念

:记为G(V,E)

有向图:每条边都有方向

无向图:边无方向

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

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

对于完全无向图,第一个节点与剩下n-1个节点相连,第二个与剩下n-2个相连……倒数第二个与最后一个相连,[(n-1)+(n-2)+......+1] = n*(n-1)/2

乘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);   //创建标记数组,初始化为0
	if (!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);   //创建标记数组,初始化为0
	if (!visited)  return;
	
	for (i = 0; i < v; i++)    //标记数组初始化为0
		visited[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

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

时间复杂度

DFS和BFS相同:

用邻接矩阵存储时,O(n^2)

用邻接表存储时,O(n+e)

四、最小生成树

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

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

4.1PRIM普利姆算法

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

表格横表头为除起点外其他点、U集合、V-U集合;竖表头为lowcost:点到U集合的最小值,adjvex:最小值相连的点

每一次选出lowcost最小的加入U集合,然后用这个节点到其它节点的距离更新lowcost和adjvex;已确定的点写0

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

#define INF 65535

typedef 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)  外层:为n个节点确定位置;内层:对于新确定的节点计算他和其他节点的距离

五、拓扑排序

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

下列 AOE 网表示一项包含 8 个活动的工程。通过同时加快若干活动的进度可以缩短整个工程的工期。下列选项中,加快其进度就可以缩短工程工期的是( )

A. c 和 e

B. d 和 e

C. f 和 d

D. f 和 h

答案:C       根据图做出ve、vl表,再做出e、l表,找到关键路径。有bdcg、bdeh、bfh三条。要缩短总工期,三条关键路径都要缩短,只有C选项能同时影响三条路径。

从本题可以看出,只看点的ve=vl不能确定关键路径 ,必须看e=l

下列关于最小生成树的说法中,正确的是()。

Ⅰ.最小生成树的代价唯一
Ⅱ.所有权值最小的边一定会出现在所有的最小生成树中
Ⅲ.使用普里姆( Prim)算法从不同顶点开始得到的最小生成树一定相同
Ⅳ.使用普里姆算法和克鲁斯卡尔( Kruskal)算法得到的最小生成树总不相同

A.仅Ⅰ

B.仅Ⅱ

C.仅Ⅰ、 Ⅲ

D.仅Ⅱ、 Ⅳ

答案:A    设想一个各边权值相等的树,则BCD错

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

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

相关文章

本地生活服务再起波澜,这些数据告诉你该选哪些行业?

当地生活领域的竞争异常激烈&#xff0c;市场形势也在发生变化&#xff0c;以"变革、拓展、创新、尝试"为中心的当地生活领域每天都有新的故事。艾瑞咨询的数据显示&#xff0c;2020年中国当地生活服务市场规模达到19.5万亿元&#xff0c;预计到2025年&#xff0c;这…

众和策略股市行情分析:为什么不建议在登记日前买入股票?

为什么不主张在挂号日前买入股票&#xff1f; 之所以不主张在挂号日前买入股票&#xff0c;是因为挂号日之后股票会除息&#xff0c;从而使得股价跌落。而挂号日前买入虽说可以享有当期分红&#xff0c;但持股达不到一定年限的&#xff0c;分红是需要付税的&#xff0c;所以不…

常用环境部署(十三)——GitLab整体备份及迁移

一、GitLab备份 注意&#xff1a;由于我的GitLab是docker安装的&#xff0c;所以我的操作都是在容器内操作的&#xff0c;大家如果不是用docker安装的则直接执行命令就行。 1、Docker安装GitLab 链接&#xff1a;常用环境部署(八)——Docker安装GitLab-CSDN博客 2、GitLab备…

DNS测试和管理工具

一、dig 命令 说明&#xff1a; &#xff08;1&#xff09;dig只用于测试dns系统&#xff0c;不会查询hosts文件进行解析。 &#xff08;2&#xff09;加"server-ip"&#xff1a;根据指定的DNS服务器来解析&#xff0c;绕过了本地解析库中设置的DNS服务器。 &…

Model::unguard()的作用

这是在生成假数据时碰见的&#xff0c;浅查了一下 Model::unguard() 是 Laravel 框架中的一个方法&#xff0c;它的作用是取消对 Eloquent 模型的属性赋值的安全性保护。 在默认情况下&#xff0c;Laravel 的 Eloquent 模型会对属性赋值做一些安全性检查&#xff0c;例如防止…

实现区域地图散点图效果,vue+echart地图+散点图

需求&#xff1a;根据后端返回的定位坐标数据实现定位渲染 1.效果图 2.准备工作,在main.js和index.js文件中添加以下内容 main.js app.use(BaiduMap, {// ak 是在百度地图开发者平台申请的密钥 详见 http://lbsyun.baidu.com/apiconsole/key */ak: sRDDfAKpCSG5iF1rvwph4Q95M…

ARM CCA机密计算架构软件栈之软件组件介绍

在本节中,您将了解Arm CCA的软件组件,包括Realm World和Monitor Root World。以下图表展示了Arm CCA系统中的软件组件: 在这个图表中,世界之间的边界显示为粗虚线。由较高权限的软件强制执行的较低权限软件组件之间的边界显示为细虚线。例如,非安全EL2处的虚拟机监视器强制…

QT上位机开发(会员管理软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了ini文件的解析办法&#xff0c;通过QSettings类就可以很轻松地访问ini文件里面的数据。除了ini文件之外&#xff0c;另外一种经常出…

安装 Node.js、npm

安装 nodejs 安装Node.js的最简单的方法是通过软件包管理器。 Node.js官网&#xff1a;https://nodejs.org/en/download/ cd /usr/local/src/wget -c https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz xz -d node-v18.16.0-linux-x64.tar.xz tar -xf node…

探究Chrome仿真模拟设备时Click区域不准确问题

一、开发环境 windows版本&#xff1a; windows 10 Chrome 版本&#xff1a; 116.0.5845.141 二、问题描述 在Chrome DevTools中开启仿真设备&#xff08;微信开发者工具也有类似问题&#xff09;&#xff0c;如果页面元素有绑定click时&#xff0c;实际点击事件响应区域会…

如何使用Docker部署Swagger Editor结合内网穿透实现远程编辑API文档

文章目录 Swagger Editor本地接口文档公网远程访问1. 部署Swagger Editor2. Linux安装Cpolar3. 配置Swagger Editor公网地址4. 远程访问Swagger Editor5. 固定Swagger Editor公网地址 Swagger Editor本地接口文档公网远程访问 Swagger Editor是一个用于编写OpenAPI规范的开源编…

【DevOps】搭建 项目管理软件 禅道

文章目录 1、简介2、环境要求3、搭建部署环境3.1. 安装Apache服务3.2. 安装PHP环境&#xff08;以php7.0为例 &#xff09;3.3. 安装MySQL服务 4、搭建禅道4.1、下载解压4.2、 配置4.2.1、 启动4.2.2、自启动4.2.3、确认是否开机启动 5、成功安装 1、简介 禅道是国产开源项目管…

一篇文章掌握系统架构的演变和常见微服务框架

目录 前言 一、系统架构的演变 1、单体应用架构 优点&#xff1a; 缺点&#xff1a; 2、垂直应用架构 优点&#xff1a; 缺点&#xff1a; 3、分布式SOA架构 3.1 什么是SOA 3.2 SOA架构 优点&#xff1a; 缺点&#xff1a; 4、微服务架构 优点&#xff1a; 缺点…

os-ten-ta-tion

在线词源词典 这是一张展现英语发展轨迹的地图。词源不是定义&#xff0c;但词源解释了我们现在所使用的单词&#xff0c;在过去的 600 年甚至 2000 年前的意思和发音是怎样的。 正文中的日期表示该单词最早留下文献记录的年份(除非另有说明&#xff0c;一般指英文文献)。但这…

【解决】电脑上的WIFI图标不见了咋整?

相信不少同学都遇到过这种情况&#xff1a;电脑上的wifi图标莫名不见了&#xff0c;甚至有时候还是在使用的中途突然断网消失的。 遇到这种情况一般有两种解决方案&#xff1a; 1. 在开机状态下长按电源键30秒以上 这种办法应该是给主板放电&#xff0c;一般应用在wifi6上面。…

Vue3-32-路由-重定向路由

什么是重定向 路由的重定向 &#xff1a;将匹配到的路由 【替换】 为另一个路由。 redirect : 重定向的关键字。 重定向的特点 1、重定向是路由的直接替换,路由的地址是直接改变的&#xff1b; 2、在没有子路由配置的情况下&#xff0c;重定向的路由可以省略 component 属性的配…

HackTheBox - Medium - Linux - Investigation

Investigation Investigation 是一款 Linux 机器&#xff0c;难度为中等&#xff0c;它具有一个 Web 应用程序&#xff0c;可为图像文件的数字取证分析提供服务。服务器利用 ExifTool 实用程序来分析图像&#xff0c;但是&#xff0c;正在使用的版本存在命令注入漏洞&#xff…

带大家做一个,易上手的家常糖醋白菜

准备 如果是大白菜就一个 小白菜就要两个 因为白菜炒完之后会变少 将白菜叶剥开每叶分成三个小块 整个剥完之后 放入盆中清洗干净 调一个糖醋汁 一勺料酒 两勺生抽 三勺白砂糖 四勺香醋 起锅烧油 放两个干辣椒 辣椒炒一下 然后倒入白菜 翻炒直到油全部融入白菜 然后倒入…

python打开文件的方式比较

open(addr,w) 打开之后文件无论以前有什么&#xff0c;打开后都要清空 /// open(addr,r) 文件打开后&#xff0c;不删除以前内容

fineBI web组件传参

1、fineBI web组件传参 1.1、 Web组件- FineBI帮助文档 FineBI帮助文档1. 概述1.1 版本FineBI 版本HTML5移动端展现功能变动6.0--V11.0.83web组件适配移动端效果优化6.0.13-web组件支持传递参数 ${过滤组件https://help.fanruan.com/finebi/doc-view-143.html 1.2、自己做的例…