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

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 1024typedef 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 1024typedef 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 graphAdjListGraph 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.hqwc.cn/news/707282.html

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

相关文章

可用在vue自动导入的插件unplugin-auto-import

在大多数vue3开发中&#xff0c;基本所有页面都会引用vue3 componsition api&#xff0c;如下代码 想这种vue3 架构中自带的api&#xff0c;如果在全局配置一下的话&#xff0c;就可以减少一部分代码量&#xff0c;只是在代码编译的时候&#xff0c;会添加相应的引用&#xff…

ArcGIS arcpy代码工具——关于标识码的那些事(查找最大标识码、唯一性检查、重排序、空值赋值)

系列文章目录 ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改 ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片 ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板 ArcGIS arcpy代码工具——定制属性表字段输出表格 ArcGIS arc…

复制即用!纯htmlcss写的炫酷input输入框

一般我们写css样式都要用样式库&#xff0c;但是嫌麻烦&#xff0c;如果能找到现成的内容复制上去就很香了&#xff0c;下文是笔者觉得好看的纯html&css写的样式&#xff0c;可以直接复制到Vue等内&#xff0c;十分方便。 input组件 1&#xff09; 下面这个很推荐&#…

C++的数据结构(九): 笛卡尔树

笛卡尔树&#xff08;Cartesian Tree&#xff09;是一种特殊的二叉树&#xff0c;其每个节点的键值&#xff08;key&#xff09;满足二叉搜索树的性质&#xff0c;即左子树上所有节点的键值小于根节点的键值&#xff0c;右子树上所有节点的键值大于根节点的键值。与此同时&…

C 深入指针(4)

目录 一、字符指针变量 1 初始化 2 与字符串数组的区别 二、数组指针变量 1 初始化 2 二维数组传参本质 三、函数指针变量 1 初始化 2 用法 四、typedef关键字 五、函数指针数组 一、字符指针变量 1 初始化 //VS2022 x64 #include <stdio.h> int main() {…

InnoDB 事务处理机制

文章目录 前言1. 事务处理挑战1.1 事务机制处理的问题1.2 并发事务带来的问题 2. InnodDB 和 ACID 模型2.1 Innodb Buffer Pool2.2 Redo log2.3 Undo log2.4 应用案例 3. 隔离级别和锁机制3.1 事务隔离级别3.1.1 READ UNCOMMITTED3.1.2 READ COMMITTED3.1.3 REPEATABLE READ3.1…

Linux下redis源码编译安装

华子目录 Redis介绍什么是RedisRedis能干什么Redis的特点Redis与memcached对比 redis源码编译安装下载源码包准备安装环境开始编译开始安装 前台启动后台启动redis开启systemctl启动redis测试redis相关知识 Redis介绍 什么是Redis 2008年&#xff0c;意大利的一家创业公司Mer…

xlrd.biffh.XLRDError: Excel xlsx file; not supported报错原因

xlrd库读取xlsx文件时报错 xlrd.biffh.XLRDError: Excel xlsx file; not supported报错原因&#xff1a; xlrd版本为2.1版本&#xff0c;需要读取xlsx文件需要安装xlrd低一些版本1.2.0版本&#xff0c;重新安装重试即可 更换xlrd版本 重新运行

数仓架构之为什么要进行数仓分层

数仓分层这个概念想必大家都很熟悉&#xff0c;不管是在实际的开发工作当中会用的&#xff0c;还是在面试官面试你的时候会问到&#xff1a;你之前的项目是按照什么分层的&#xff0c;分哪几层&#xff0c;数仓分层有什么好处&#xff0c;举个栗子说说。 简而言之&#xff0c;…

element-ui dialog form 弹框表单组件封装

在使用 element-ui 进行后端管理系统开发时&#xff0c;在封装弹框表单时&#xff0c;遇到两个问题&#xff0c;这里进行简单记录&#xff1a; 1、问题一&#xff1a;点击关闭按钮及遮罩层关闭弹框时&#xff0c;页面报错&#xff0c;如下&#xff1a; 子组件封装&#xff1a;…

上海初中生古诗文大会倒计时4个月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;备考要趁早&#xff0c;因为知识点还是相对比较多的。这些知识点对于初中语文的学习也是很有帮助的。 今天我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是…

普通人也能创业!轻资产短视频带货项目,引领普通人实现创业梦想

在这个信息爆炸的时代&#xff0c;创业似乎成为了越来越多人的梦想。然而&#xff0c;传统的创业模式 keJ0277 往往伴随着高昂的资金投入和复杂的管理流程&#xff0c;让许多普通人望而却步。然而&#xff0c;现在有一种轻资产短视频带货项目正在悄然兴起&#xff0c;它以其低…