C++ 数据结构算法 学习笔记(25) - 图及其企业级应用

news2024/11/26 23:24:02

C++ 数据结构算法 学习笔记(25) - 图及其企业级应用

图的故事导入

故事情节

Jack 自从买车后,交通出行方便了,心自然就野了!身边的各种朋友自然就多了起来! 有一天晚上,一个年轻漂亮的女同事生日,Jack 受邀请准时爽约!Jack 亲睐此女已久,只是 介于家里的 LD 不敢越雷池一步,但是,一有机会,Jack 都会和此女接近,经常在一起,所以可 以说是就当个哥们吧! Jack 当晚开心的和女同事喝酒、聊天,忘记了家的存在,不知不觉时间一下子指向了 10 点! 此时,正沉浸在幻想中的 Jack 被老婆电话惊醒!不用接都知道,老婆的“圣旨”又来了! 这下麻烦来了,老婆电话一到,必须半小时内到家,否则,皮都可能被母老虎拔了,但现在自己 渴了酒,叫代驾又需要很久的时间,这一想, Jack 觉得自己麻烦来了!

在这里插入图片描述

做为程序员,Jack 肯定会选择第 3 条,那么我们如何用程序来做选择呢?

图的原理精讲

在计算机科学中,一个图就是一些顶点的集合,这些顶点通过一系列边结对(连接)。顶点用圆圈表示,边就 是这些圆圈之间的连线。顶点之间通过边连接。注意:顶点有时也称为节点或者交点,边有时也称为链接。 社交网络,每一个人就是一个顶点,互相认识的人之间通过边联系在一起, 边表示彼此的关系。这种关系可以 是单向的,也可以是双向的!

在这里插入图片描述

同时,边可以是双向的,也可以是单向的!

我们前面讲解的树和链表都是图的特例!

在这里插入图片描述

如果我们有一个编程问题可以通过顶点和边表示,那么我们就可以将你的问题用图画出来,然后使用相应的图 算法来找到解决方案。

图的表示

领接链表

在邻接列表实现中,每一个顶点会存储一个从它这里开始的相邻边的列表。比如,如果顶点 B 有一条边到 A、 C 和 E,那么 A 的列表中会有 3 条边

在这里插入图片描述

邻接列表只描述指向外部的边。B 有一条边到 A,但是 A 没有边到 B,所以B没有出现在A的邻接列表中。 查找两个顶点之间的边或者权重会比较费时,因为遍历邻接列表直到找到为止。

领接矩阵

由二维数组对应的行和列都表示顶点,由两个顶点所决定的矩阵对应元素数值表示这里两个顶点是否相连(如, 0 表示不相连,非 0 表示相连和权值)、如果相连这个值表示的是相连边的权重。例如,广西到北京的机票, 我们用邻接矩阵表示:

在这里插入图片描述

往这个图中添加顶点的成本非常昂贵,因为新的矩阵结果必须重新按照新的行/列创建,然后将已有的数据复制到新的矩阵中。

在这里插入图片描述

结论:大多数时候,选择邻接链表是正确的。(在图比较稀疏的情况下,每一个顶点都只会和少数几个顶点相连,这种情况下邻接列表是最佳选择。如果这个图比较密集,每一个顶点都和大多数其他顶点相连,那么邻接矩阵更合适。)

图的算法实现

在这里插入图片描述

邻接表结构的定义

#define MaxSize 1024

typedef struct _EdgeNode {//与节点连接的边的定义
	int adjvex; //邻接的顶点
	int weight; //权重
	struct _EdgeNode* next; //下一条边
}EdgeNode;

typedef struct _VertexNode {//顶点节点
	char data; //节点数据
	struct _EdgeNode* first;//指向邻接第一条边
}VertexNode, AdjList;

typedef struct _AdjListGraph {
	AdjList* adjlist;
	int vex; //顶点数
	int edge; //边数
}AdjListGraph;

邻接表的初始化

void Init(AdjListGraph& G) {
	G.adjlist = new AdjList[MaxSize];
	G.edge = 0;
	G.vex = 0;

	for (int i = 0; i < MaxSize; i++)
	{
		visited[i] = false;
	}
}

邻接表的创建

void Create(AdjListGraph& G) {
	cout << "Please enter the graph algorithm vertices and node number" << endl;
	cin >> G.vex >> G.edge;
	cout << "Please enter the vertices node data" << endl;
	for (int i = 0; i < G.vex; i++) {
		cin >> G.adjlist[i].data;
		G.adjlist[i].first = NULL;
	}
	char v1 = 0, v2 = 0;//保存输入的顶点的字符
	int i1, i2; //保存顶点在数组中的下标
	int weight;
	cout << "Please enter the related vertices data to connect them:" << endl;

	for (int i = 0; i < G.edge; i++) {
		cin >> v1 >> v2>>weight;
		i1 = Location(G, v1);
		i2 = Location(G, v2);
		if (i1 != -1 && i2 != -1) {//寻找到位置
			EdgeNode* temp = new EdgeNode;
			temp->adjvex = i2;
			temp->next = G.adjlist[i1].first;
			temp->weight = weight;
			G.adjlist[i1].first = temp;
		}
	}
}

/*通过顶点对应的字符寻找顶点在图中的邻接点*/
int Location(AdjListGraph& G, char c) {
	for (int i = 0; i < G.vex; i++) {
		if (G.adjlist[i].data == c) {
			return i;
		}
	}
	return -1;
}

深度优先遍历思想

  • 首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;
  • 当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直到所有的顶点都被访问过。

在这里插入图片描述

使用深度优先搜索来遍历这个图的具体过程是:

  1. 首先从一个未走到过的顶点作为起始顶点,比如 A 顶点作为起点。
  2. 沿 A 顶点的边去尝试访问其它未走到过的顶点,首先发现 E 号顶点还没有走到过,于是访问E顶点
  3. 再以 E 顶点作为出发点继续尝试访问其它未走到过的顶点,接下来访问 D 顶点。
  4. 再尝试以 D 顶点作为出发点继续尝试访问其它未走到过的顶点。
  5. 但是,此时沿 D 顶点的边,已经不能访问到其它未走到过的顶点,接下来返回到 E 顶点。
  6. 返回到 E 顶点后,发现沿 E 顶点的边也不能再访问到其它未走到过的顶点。此时又回到顶点 A(D->E->A),再以 A 顶点作为出发点继续访问其它未走到过的顶点,于是接下来访问 C 顶点
  7. 最终访问的结果是 A -> E -> D ->B

代码实现

void DHS(AdjListGraph& G, int v)
{
	if (visited[v] == true) return;

	int index = -1;
	cout << G.adjlist[v].data << " ";
	visited[v] = true;

	EdgeNode* tmp = G.adjlist[v].first;
	if (tmp)
	{
		index = tmp->adjvex;
		DHS(G, index);
		tmp = tmp->next;
	}
}

void DHS_Main(AdjListGraph& G)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (visited[i] == false)
		{
			DHS(G, i);
		}
	}
}

广度优先遍历思想

  • 首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点;
  • 然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束
    在这里插入图片描述
void BHS(AdjListGraph& G, int v)
{

	int index = -1;
	int cur = -1;
	queue<int>q;
	q.push(v);
	while (!q.empty())
	{
		cur = q.front();
		if (visited[cur] == false)
		{
			cout << G.adjlist[cur].data << " ";
			visited[cur] = true;
		}
		q.pop();
		EdgeNode* temp = G.adjlist[cur].first;

		while (temp)
		{
			index = temp->adjvex;
			q.push(index);
			temp = temp->next;
		}
	}
}

void BHS_Main(AdjListGraph& G)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (visited[i] == false)
		{
			BHS(G, i);
		}
	}
}

完整代码

#include <iostream>
#include <string>
#include <queue>

using namespace std;

#define MAX_SIZE	1024
bool visited[MAX_SIZE];
typedef struct _EdgeNode
{
	int adjvex;
	int weight;
	struct _EdgeNode* next;
}EdgeNode;

typedef struct _VertexNode
{
	char data;
	EdgeNode* first;

}VertexNode, AdjList;

typedef struct _AdjListGraph
{
	int edge;
	int vex;
	AdjList* adjlist;
}AdjListGraph;

bool Init_Graph(AdjListGraph& G)
{
	G.adjlist = new AdjList [MAX_SIZE];
	if (!G.adjlist)
	{
		cout << "Failed to allocate memory for the AdjGraph" << endl;
		return true;
	}
	G.edge = 0;
	G.vex = 0;

	for (int i = 0; i < MAX_SIZE; i++)
	{
		visited[i] = false;
	}
	return true;
}

int Location(AdjListGraph& G, char data)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (G.adjlist[i].data == data)
		{
			cout << "The Location is sucessfully found" << endl;
			return i;
		}
	}
	return -1;
}

bool Create_Graph(AdjListGraph& G)
{
	cout << "Please enter the number of vertices and edge for the ListGraph" << endl;
	cin >> G.vex >> G.edge;

	cout << "Please enter the vex data for the ListGraph accrodingly" << endl;
	for (int i = 0; i < G.vex; i++)
	{
		cin >> G.adjlist[i].data;
		G.adjlist[i].first = NULL;
	}

	char v1, v2;
	int i1, i2;
	int weight;

	cout << "Please enter the related data to connect them" << endl;
	for (int i = 0; i < G.edge; i++)
	{
		cin >> v1 >> v2 >>weight;
		i1 = Location(G, v1);
		i2 = Location(G, v2);

		if (i1 != -1 && i2 != -1)
		{
			EdgeNode* new_node = new EdgeNode;
			new_node->adjvex = i2;
			new_node->next = G.adjlist[i1].first;
			new_node->weight = weight;
			G.adjlist[i1].first = new_node;
			
		}
	}
	return true;
}

void DHS(AdjListGraph& G, int v)
{
	if (visited[v] == true) return;

	int index = -1;
	cout << G.adjlist[v].data << " ";
	visited[v] = true;

	EdgeNode* tmp = G.adjlist[v].first;
	if (tmp)
	{
		index = tmp->adjvex;
		DHS(G, index);
		tmp = tmp->next;
	}
}

void DHS_Main(AdjListGraph& G)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (visited[i] == false)
		{
			DHS(G, i);
		}
	}
}

void BHS(AdjListGraph& G, int v)
{

	int index = -1;
	int cur = -1;
	queue<int>q;
	q.push(v);
	while (!q.empty())
	{
		cur = q.front();
		if (visited[cur] == false)
		{
			cout << G.adjlist[cur].data << " ";
			visited[cur] = true;
		}
		q.pop();
		EdgeNode* temp = G.adjlist[cur].first;

		while (temp)
		{
			index = temp->adjvex;
			q.push(index);
			temp = temp->next;
		}
	}
}

void BHS_Main(AdjListGraph& G)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (visited[i] == false)
		{
			BHS(G, i);
		}
	}
}

int main()
{
	AdjListGraph G;
	Init_Graph(G);
	Create_Graph(G);

	BHS_Main(G);
	system("pause");
	return 0;
}

图的导航-最短路径算法 (较复杂,可跳过)

从起点开始访问所有路径,则到达终点节点的路径有多条,其中路径权值最短的一条则为最短路径。最短路径算法有 深度优先遍历、广度优先遍历、Bellman-Ford 算法、弗洛伊德算法、 SPFA(Shortest Path Faster Algorithm)算法和迪 杰斯特拉算法等。

在这里插入图片描述

代码实现

#include <iostream>
#include <string>
#include <queue>

using namespace std;

#define MaxSize 1024

typedef struct _EdgeNode {//与节点连接的边的定义
	int adjvex; //邻接的顶点
	int weight; //权重
	struct _EdgeNode* next; //下一条边
}EdgeNode;

typedef struct _VertexNode {//顶点节点
	char data; //节点数据
	struct _EdgeNode* first;//指向邻接第一条边
}VertexNode, AdjList;

typedef struct _AdjListGraph {
	AdjList* adjlist;
	int vex; //顶点数
	int edge; //边数
}AdjListGraph;

bool visited[MaxSize]; //Global array to record whether the node is being visited

/*图的初始化*/
void Init(AdjListGraph& G) {
	G.adjlist = new AdjList[MaxSize];
	G.edge = 0;
	G.vex = 0;

	for (int i = 0; i < MaxSize; i++)
	{
		visited[i] = false;
	}
}

int Location(AdjListGraph& G, char c);

void Create(AdjListGraph& G) {
	cout << "Please enter the graph algorithm vertices and node number" << endl;
	cin >> G.vex >> G.edge;
	cout << "Please enter the vertices node data" << endl;
	for (int i = 0; i < G.vex; i++) {
		cin >> G.adjlist[i].data;
		G.adjlist[i].first = NULL;
	}
	char v1 = 0, v2 = 0;//保存输入的顶点的字符
	int i1, i2; //保存顶点在数组中的下标
	int weight;
	cout << "Please enter the related vertices data to connect them:" << endl;

	for (int i = 0; i < G.edge; i++) {
		cin >> v1 >> v2>>weight;
		i1 = Location(G, v1);
		i2 = Location(G, v2);
		if (i1 != -1 && i2 != -1) {//寻找到位置
			EdgeNode* temp = new EdgeNode;
			temp->adjvex = i2;
			temp->next = G.adjlist[i1].first;
			temp->weight = weight;
			G.adjlist[i1].first = temp;
		}
	}
}

/*通过顶点对应的字符寻找顶点在图中的邻接点*/
int Location(AdjListGraph& G, char c) {
	for (int i = 0; i < G.vex; i++) {
		if (G.adjlist[i].data == c) {
			return i;
		}
	}
	return -1;
}

int min_weights = 0x7FFFFFFF;
int steps = 0;
int path[MaxSize] = { 0 };
int shortest_path[MaxSize] = {0};   //Save the shortest path

//According the graph to do the deep visit function
void DFS(AdjListGraph& G, int start, int end, int weights)
{
	int cur = -1;
	if (start == end)
	{
		for (int i = 0; i < steps; i++)
		{
			cout << G.adjlist[path[i]].data << " ";   //Print the posible path
		}

		cout << "The posible path length is : " << weights;
		if (min_weights > weights)
		{
			min_weights = weights;
			memcpy(shortest_path, path, steps*sizeof(int));
		}
		return;
	}

	visited[start] = true;
	EdgeNode* temp = G.adjlist[start].first;

	while (temp)
	{
		int weight = temp->weight;
		cur = temp->adjvex;

		if (visited[cur] == false)
		{
			visited[cur] = true; 
			path[steps++] = cur;
			DFS(G, cur, end, weights + weight);
			visited[cur] = false;
			path[--steps] = 0;
		}
		temp = temp->next;
	}
}


void BFS(AdjListGraph& G, int v)
{
	queue<int> q;
	int cur = -1;
	int index =-1;
	q.push(v);
	while (!q.empty()) 
	{
		cur = q.front();
		if (visited[cur] == false)
		{
			cout << G.adjlist[cur].data << " ";
			visited[cur] = true;

		}
		q.pop();
		EdgeNode* temp = G.adjlist[cur].first;
		while (temp != NULL)
		{
			index = temp->adjvex;
			temp = temp->next;
			q.push(index);

		}
	}

}

void BFS_Main(AdjListGraph& G)
{
	for (int i = 0; i < G.vex; i++)
	{
		if (visited[i] == false)
		{
			BFS(G, i);
		}
	}
}
int main()
{
	//Initiallized the graph
	AdjListGraph G;
	Init(G);

	Create(G);

	char start, end;
	cout << "Please enter the minumum path for the starting point and ending point" << endl;
	cin >> start >> end;

	DFS(G,Location(G,start),Location(G,end),0);
	cout << "The shortest path length is" << min_weights << endl;
	cout << "Path : ";
	int i = 0;
	while (i < MaxSize && shortest_path[i]>0)
	{
		cout << G.adjlist[shortest_path[i]].data << " ";
		i++;
	}
	cout << endl;
	system("pause");
	return 0;
}

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

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

相关文章

python 自定义包的实现

1. 代码目录 创建自定义包的时候&#xff0c;原理是当 python 检测到一个目录下存在 __init__.py 文件时&#xff0c;python 就会把它当成一个模块(module)。 下面这个例子是网上整理的代码&#xff0c;但是有些小改动&#xff0c;可以直接拿来就用。 看代码结构&#xff1a;…

springboot+vue+mybatis社交网络平台+PPT+论文+讲解+售后

近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社交网络平台利用计算机网络实现信息化管理&#xff0c;使整个社交网络管理的发展和服务水平有显著提升。 本文拟采用Eclipse开发工具…

免费证件照一键换底色

最近星期天在家搞了一个小工具&#xff0c;在这里分享下! 废话不多说看看效果&#xff1a; 效果还不错&#xff0c;需要的可以联系我!!!!!!!!! 别的网上可都是一次五块钱这种。太贵了。。&#xff01;&#xff01;

Python - 深度学习系列33 - ollama_langchain_ppt生成

说明 只是为了速记一下这个实践过程。整体上说&#xff0c;这个结果并不是那么好用&#xff0c;但有一些可以借鉴的地方。 先看结果&#xff1a; 生成的PPT 说的直白点&#xff0c;就是用大模型生成了一堆没太有意义的文字&#xff0c;然后做成ppt。所以实用是不成的&#…

Golang | Leetcode Golang题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; func maximalRectangle(matrix [][]byte) (ans int) {if len(matrix) 0 {return}m, n : len(matrix), len(matrix[0])left : make([][]int, m)for i, row : range matrix {left[i] make([]int, n)for j, v : range row {if v 0 {continu…

【Spring Boot】Spring Boot 中的 Starter

Spring Boot 中的 Starter 1.常用 Starter2.为什么要用 Starter3.Starter 有哪些要素 我们都知道&#xff0c;Spring 的功能非常强大&#xff0c;但也有些弊端。比如&#xff1a;我们需要手动去配置大量的参数&#xff0c;没有默认值&#xff0c;需要我们管理大量的 jar 包和它…

SAP-CentralFinance - 学习心得2

过账总账中的交易 业务示例 创建大量日记账分录是会计日常工作的一部分。在SAP,会计可以使用不同的输入屏幕。使用所有方法,总账科目过账会自动列在损益表报表中(如果财务报表版本中包含科目)。查询已过账科目时还可显示对应的过账。 贵公司计划通过企业基金增加资本。在…

kafka安装配置及集成springboot

1. 安装 单机安装kafka Kafka对于zookeeper是强依赖&#xff0c;保存kafka相关的节点数据&#xff0c;所以安装Kafka之前必须先安装zookeeper dockerhub网址: https://hub.docker.com Docker安装zookeeper 下载镜像&#xff1a; docker pull zookeeper:3.4.14创建容器 doc…

《C++学习笔记---初阶篇6》---string类 上

目录 1. 为什么要学习string类 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 2.2.1. string类对象的常见构造 2.2.2. string类对象的容量操作 2.2.3.再次探讨reserve与resize 2.2.4.string类对象的访问及遍历操作 2.2.5…

PPMP_char3

PMPP char3 – Multidimensional grids and data ​ 五一过后&#xff0c;有些工作要赶&#xff0c;抽出时间更新一下。这一章基本都熟练掌握&#xff0c;在做习题过程中有一些思考。这里涉及到了一点点GEMM&#xff08;矩阵乘&#xff09;&#xff0c;GEMM有太多可深挖的了&a…

Ubuntu24 文件目录结构——用户——权限 详解

目录 权限 用户 文件目录结构 一个目录可以有程序&#xff0c;目录&#xff0c;文件&#xff0c;以及这三者的链接。可以看到还分别有使用者和权限信息。 每个文件和目录都有与之关联的三个主要属性&#xff1a;所有者&#xff08;owner&#xff09;、组&#xff08;group&a…

【ESP32接入ATK-MO1218 GPS模块】

【ESP32接入ATK-MO1218 GPS模块】 1. 引言2. ATK-MO1218 GPS模块概述3. 接入ATK-MO1218 GPS模块的步骤4. 示例代码5. 结论1. 引言 在现代的嵌入式系统和物联网项目中,精确的位置信息是至关重要的。ATK-MO1218 GPS模块作为一款高性能的GPS/北斗双模定位模块,为开发者提供了强…

【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 容器类控件 | Tab Widget的使用及说明 文章编号&#xf…

实现红黑树

目录 红黑树的概念 红黑树的节点结构定义 红黑树的插入 红黑树的验证 实现红黑树完整代码 红黑树的概念 红黑树 &#xff0c;是一种 二叉搜索树 &#xff0c;但 在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red 或 Black 。 通过对 任何一条从根到叶子的…

You Only Cache Once:YOCO 基于Decoder-Decoder 的一个新的大语言模型架构

这是微软再5月刚刚发布的一篇论文提出了一种解码器-解码器架构YOCO&#xff0c;因为只缓存一次KV对&#xff0c;所以可以大量的节省内存。 以前的模型都是通过缓存先前计算的键/值向量&#xff0c;可以在当前生成步骤中重用它们。键值(KV)缓存避免了对每个词元再次编码的过程&…

基于SSM的“网约车用户服务平台”的设计与实现(源码+数据库+文档)

基于SSM的“网约车用户服务平台”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能 首页 站内新闻浏览 打车信息查询功能 在线打车功能…

Linux 服务器配置共享文件夹(NFS)

一、准备三台 linux 服务器 三台服务器: manger:172.16.11.178 ap1:172.16.11.179 ap2:172.16.11.180 /root/serverfiles/ 为共享目录 二、配置步骤 1、在服务端01的机器上安装nfs和rpcbind程序 yum -y install nfs* yum -y install rpcbind* 2、在安装完nfs以及rpcb…

MySQL查询篇-聚合函数-窗口函数

文章目录 distinct 关键字聚合函数常见的聚合函数group by和having 分组过滤 窗口函数with as窗口聚合函数排名窗口函数值窗口函数 distinct 关键字 distinct 去重数据&#xff0c;ps:null值也会查出来 select distinct column from table;聚合函数 常见的聚合函数 select …

【保姆级教程】VMware Workstation Pro的虚拟机导入vritualbox详细教程

解决方案 1、OVF格式2、VMX格式 1、OVF格式 选定需要导出的虚拟机&#xff08;关闭或者挂起状态下&#xff09;依次选择文件-导出为ovf 在Vritualbox导入刚刚导出的.ovf文件 更改路径&#xff0c;按实际需要修改 成功导入 2、VMX格式 如果在VMware Workstation Pro导出的…

rs6(vmp)瑞某,药某局,商某局,专某局,维某网,cookie + 后缀 的分析解析

文章目录 说在前面rs vmp 特征 介绍解决方法算法补环境运行报错 代码联调补环境框架 补环境导出结果导出cookie导出后缀 效果展示 vx lyj_txd qq 1416279170 # 加我备注来意说在前面 免责声明&#xff1a; 本篇文章只做学习讨论&#xff0c;无商务用途&#xff0c; 未对目标…