数据结构----结构--非线性结构--图

数据结构----结构–非线性结构–图

一.图(Graph)

1.图的结构

图是多对多的结构

2.图的基本概念

图的基本概念为G=(V,E)

V是顶点的集合

E是边的集合

G是图

一个图其实是顶点和边的二元组合

观察下图,思考V和E集合中的元素分别都是什么

在这里插入图片描述

V={V1,V2,V3,V4,V5}

E={(V1,V2),(V1,V3),(V1,V5),(V2,V3),(V2,V5),(V3,V4),(V3,V5),(V4,V5)}

G是V和E的二元组合

注意:图中更重要的是边(顶点与顶点之间的关系)

3.图的一些相关概念

这里将无向图和有向图进行对比说明(如果有相同的概念那么不会在有向图中重复书写)

无向图:
1.边的表示方法为(V1,V2)
了解:

依附:V1到V2这条边就是依附于V1和V2这两个顶点的

2.完全图:图中所有可能存在的边都存在
关于完全图的一个知识点

n个节点的无向完全图边有Cn2个(n在C的右下角,2在n的左上角)

3.路径:一个点到另外一个点所经过的边的总数
4.简单路径:除起点和终点可能相同以外不允许顶点重复出现
5.连通:一个点到另一个点可通
6.连通图:图中任意两点均有路径可通
7.子图:如果G1是G的子图,那么V1∈V,E1∈E
8.度:图中的度指当前节点关联的边的长度
极大连通子图(也叫连通分量),对于一个连通图来讲它的极大连通子图就是1,再看下面的一张图进行理解

在这里插入图片描述

此图中的极大连通子图为2

有向图:
1.边的表示方法为 <V1,V2>
2.完全图:图中所有可能存在的边都存在
关于完全图的一个知识点

n个节点的无向完全图边有An2个(n在C的右下角,2在n的左上角)

3.强连通:一个点到另一个点双向可通
4.连通图:图中任意两点均有路径双向可通
5.有向图的边也叫弧,箭头的那一端叫弧首,另一端叫弧尾
8.度:有向图中指出去的边的总数叫入度,指进来的边的总数叫入度,出度入度之和就是度

3.图的存储方式

1.图的存储方式有两种,一种是用邻接矩阵(二维数组)进行存储,一种是用邻接链表进行存储

将下图分别用邻接矩阵和邻接链表进行存储

在这里插入图片描述

1.邻接矩阵(二维数组)

如下图

在这里插入图片描述

顶点之间没有边的正常应该是无穷这里用0来进行代替

行代表指向谁,如第一行代表V1指向V2,V3,V5

列代表被谁指向,如第一列代表V1被V2,V3,V5指向

注意:如果一个图的邻接矩阵沿对角线对称且无特殊声明那这个图就是无向图
2.邻接链表

如下图

在这里插入图片描述

2.两种存储方式应用场合的不同

邻接矩阵适用于边多的图

邻接链表适用于边少的图

3.两种存储方式的区别

邻接矩阵是唯一的

邻接链表不唯一

4.用邻接矩阵(二维数组)实现图的存储(代码实现)
操作步骤

1.创建一个结构体(此结构体中的属性有顶点的个数,边的个数,指向邻接矩阵的指针)

1.得到顶点的个数,边的个数

2.申请一个邻接矩阵(动态申请空间,记得要赋初值)

3.添加边

代码如下
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node {int nVertex;int nEdge;int* pMatrix;
}Graph;Graph* CreateGraph() {//申请一个结构体类型的变量Graph * pGraph= (Graph*)malloc(sizeof(Graph));int nV;int nE;printf("请输入点数\n");scanf_s("%d", &nV);printf("请输入边数\n");scanf_s("%d", &nE);pGraph->nVertex = nV;pGraph->nEdge = nE;//申请矩阵pGraph->pMatrix= (int*)malloc(sizeof(int) * nV * nE);memset(pGraph->pMatrix, 0, sizeof(int) * nV * nE);//添加边int v1, v2;int i;for (int i = 0; i < nE; i++) {printf("输入两个顶点确定一条边\n");scanf_s("%d%d", &v1, &v2);if (v1 != v2 && v1 >= 1 && v1 <= nV && v2 >= 1 && v2 <= nV && pGraph->pMatrix[(v1 - 1) * nV + (v2 - 1)] == 0) {//如果符合创建边的条件,那么创建这条边pGraph->pMatrix[(v1 - 1) * nV + (v2 - 1)] = 1;pGraph->pMatrix[(v2 - 1) * nV + (v1 - 1)] = 1;}else {//如果输入的两个顶点不符合这个图创建边的条件,那么这里的i变量的值不会增大,以此保证边的总数不变i--;}}return pGraph;
}
int main() {Graph* pGraph = CreateGraph();for (int i = 0; i < pGraph->nVertex * pGraph->nVertex; i++) {if (i % pGraph->nVertex == 0) printf("\n");printf("%d ", pGraph->pMatrix[i]);}return 0;
}

二.BFS和DFS

图的广度优先遍历就是BFS

图的深度优先遍历就是DFS

1.深度/深度优先的底层算法–回溯法(BackTracking)

1.回溯法解决的问题

1.求子集的问题

2.求排列的问题

3.求集合的问题

4.求棋盘的问题

2.回溯常见的写法

循环嵌套递归

3.用回溯法解决一道全排列的问题(此题的网址为https://leetcode.cn/problems/permutations/)
题目:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

方法一:标记法
方法二:交换法
用方法一来解决此问题
代码如下
//下面代码不是明显的标记写法,但是和标记的思路相同
class Solution {
public:void BT(vector<vector<int>>& result, vector<int> nums, vector<int> temp, int x) {//如果这个排列是全排列的一个答案(这里是以是否达到答案排列的长度进行判断的) if (temp.size() == x) {result.push_back(temp);return;}//对可能的元素进行选择(每次选择一个元素放到排列的当前位置上)for (int i = 0; i < nums.size(); i++) {temp.push_back(nums[i]);vector<int>::iterator ite = nums.begin();ite += i;ite = nums.erase(ite);//继续看当前排列的下一个位置BT(result, nums, temp, x);//为了当前排列当前位置下一个元素的正常判断,进行还原ite = temp.end()-1;vector<int>::iterator ite2 = nums.begin();nums.insert(ite2, *ite);temp.pop_back();}}vector<vector<int>> permute(vector<int>& nums) {vector<vector<int>> result;vector<int> temp;BT(result,nums,temp,nums.size());return result;}
};

2.DFS:深度优先遍历

操作步骤:

1.申请一个标记数组,赋初值

2.从给出的顶点开始遍历

​ (1)打印当前顶点

​ (2)对当前顶点进行标记(就是给标记数组相应的位置进行值的修改)

​ (3)找当前顶点邻接的且未标记的顶点进行处理,重复2

代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node {int nVertex;int nEdge;int* pMatrix;
}Graph;Graph* CreateGraph() {//申请一个结构体类型的变量Graph* pGraph = (Graph*)malloc(sizeof(Graph));int nV;int nE;printf("请输入点数\n");scanf_s("%d", &nV);printf("请输入边数\n");scanf_s("%d", &nE);pGraph->nVertex = nV;pGraph->nEdge = nE;//申请矩阵pGraph->pMatrix = (int*)malloc(sizeof(int) * nV * nE);memset(pGraph->pMatrix, 0, sizeof(int) * nV * nE);//添加边int v1, v2;int i;for (int i = 0; i < nE; i++) {printf("输入两个顶点确定一条边\n");scanf_s("%d%d", &v1, &v2);if (v1 != v2 && v1 >= 1 && v1 <= nV && v2 >= 1 && v2 <= nV && pGraph->pMatrix[(v1 - 1) * nV + (v2 - 1)] == 0) {//如果符合创建边的条件,那么创建这条边pGraph->pMatrix[(v1 - 1) * nV + (v2 - 1)] = 1;pGraph->pMatrix[(v2 - 1) * nV + (v1 - 1)] = 1;}else {//如果输入的两个顶点不符合这个图创建边的条件,那么这里的i变量的值不会增大,以此保证边的总数不变i--;}}return pGraph;
}void MyDFS(Graph* pGraph, int begin, char* flagArr) {//打印printf("%d ", begin);//标记flagArr[begin - 1] = 1;//遍历当前顶点的邻接点for (int i = 0; i < pGraph->nVertex; i++) {if (pGraph->pMatrix[(begin - 1) * pGraph->nVertex + i] == 1 && flagArr[i] != 1) {//如果是邻接且未处理的顶点,那么就遍历此顶点MyDFS(pGraph, i + 1, flagArr);}}
}void DFS(Graph* pGraph,int begin) {//申请一个标记数组char* flagArr = (char*)malloc(sizeof(char) * pGraph->nVertex);memset(flagArr, 0, sizeof(char) * pGraph->nVertex);MyDFS(pGraph, begin, flagArr);
}int main() {Graph* pGraph = CreateGraph();//进行DFS遍历DFS(pGraph, 2);return 0;
}

3.BFS:广度优先遍历

操作步骤:

1.申请一个标记数组和一个队列,给标记数组赋初值

2.将遍历的第一个顶点放入到队列中,并对此顶点进行标记(就是给标记数组相应的位置进行值的修改)

3.将队列中的首元素弹出,输出,将弹出顶点的邻接的且未标记的顶点入队,并对此顶点进行标记,然后重复3,直到队列为空此操作结束

代码如下:
void BFS(Graph* pGraph, int begin) {if (pGraph == NULL || begin<1 || begin > pGraph->nVertex)return;//申请一个标记数组char* flagArr = (char*)malloc(sizeof(char) * pGraph->nVertex);memset(flagArr, 0, sizeof(char) * pGraph->nVertex);//申请一个队列queue<int> qe;//起点入队qe.push(begin);flagArr[begin - 1] = 1;//对队列进行处理while (!qe.empty()) {//弹出 打印int x = qe.front();cout<<x;qe.pop();//邻接点for (int i = 0; i < pGraph->nVertex; i++) {if (pGraph->pMatrix[(x - 1) * pGraph->nVertex + i] == 1 && flagArr[i] != 1) {//入队 标记qe.push(i + 1);flagArr[i] = 1;}}}//释放空间free(flagArr);flagArr = NULL;
}int main() {Graph* pGraph = CreateGraph();//进行BFS遍历BFS(pGraph, 1);return 0;
}

三.迪杰斯特拉算法(Dijkstra)

1.迪杰斯特拉算法的用途

它可以求在有向的、带正权值的图中,点与点之间的最短路径

2.迪杰斯特拉算法的实现用到了什么基础算法

迪杰斯特拉的实现用到了贪心的算法

什么是贪心

在有多个选择的时候不考虑长远的情况,只考虑眼前的这一步,在眼前这一步选择当前的最好的方案

3.看下面例子进一步理解迪杰斯特拉

1.题目:求出下图中从V1到其他各个顶点的最短路径

在这里插入图片描述

2.进行解决
操作步骤:

1.画出该图的邻接矩阵如下图

在这里插入图片描述

2.我们从上图可以知道V1到各个顶点的路径大小为{X,6,31,15}(注意集合中分别表示V1到各个顶点的距离,到自己本身为X,因为不需要算到自己的距离)

​ 那我们运用贪心的算法取当前V1所到顶点最短的那个顶点V2(且V1到此顶点的最短距离就是当前的这个距离,之后不需要再进行改变了),然后从V1->V2再从V2到其他的各个点(如果此时所得的路径小的话,就对集合进行更新,),得到的新的路径大小为{X,6,31,9}

​ 然后再从集合中选择一个最小的且没有选择过的顶点,这里选择的是V4(且V1到此顶点的最短距离就是当前的这个距离,之后不需要再进行改变了),那此时就是先从V1->V4,再从V4到其他的各个点(如果此时所得的路径小的话,就对集合进行更新),得到的新的路径大小为{X,6,31,9}

​ 重复选择一个最小的且没有选择过的顶点(且V1到此顶点的最短距离就是当前的这个距离,之后不需要再进行改变了),直到所有顶点都被选择过了,就会得到V1到其他各个顶点的最短路径,结束操作

4.迪杰斯特拉的时间复杂度

使用迪杰斯特拉的时间复杂度为O(n的平方)

了解:弗洛伊德算法是求多个点中任选两点之间的最短距离

四.最小生成树

1.最小生成树可以解决的问题

将一个连通图的总长度变成最小的且该图还是一个连通图

注意:图和树的一个区别就是图可以有环,树中不能有环
所以为什么该操作叫最小生成树就可以用上面这个知识点来解释,这里的操作其实就是选择最小的边保证各个顶点是连接的,而这种操作也会导致图中不会出现环,所以该操作叫最小生成树

2.实现最小生成树的两种方法

1.克鲁斯卡尔(Kruscal)

克鲁斯卡尔是从边出发进行操作的

操作步骤:

1.先将之前图中的所有顶点都画出来

2.对图中所有的边进行排序,选择最小的边,添加到图中(注意此边的连接的两个垫顶点和之前图中的一样),依次选择直到所有的顶点都连上了结束(注意如果将一条添加到图中后,形成了环,那么这条边我们不用,舍弃掉,继续找下一个最小的边)

2.普利姆(Prim)

普利姆是从点出发进行操作的

操作步骤:

1.任选一个顶点,然后从当前顶点到另外顶点的边中,找到那条最小的边,然后在图中添加这条边和这条边连接的顶点

2.然后从当前所有顶点到另外所有顶点的边中,找到那条最小的边,然后在图中添加这条边和这条边连接的顶点(注意如果找到的边所连接的那个顶点式已经存在的那么这条边和这个顶点舍弃掉,因为如果使用了那就会形成环了),重复操作2直到所有的顶点都连上了结束

五.拓扑排序(Topological Order)

1.拓扑排序的服务结构

拓扑排序的服务结构是有向无环图(DAG )(全称为Directed Acylic Graph)

注意:它们两个是充分必要条件,拓扑排序能验证它所服务的结构是不是DAG,并且它也只能为DAG服务

2.拓扑排序的功能

为一个项目中具备依赖关系的多个活动求得可执行的线性顺序

2.看一个用拓扑排序解决的问题,更好的理解拓扑排序

1.题目:

小李的课程表如下,你该如何给他安排课程,才能让他把所有的课修完

在这里插入图片描述

2.解决步骤如下:
1.根据上图中的课程(作为图中的顶点)和学某个课程所需要先学的课程(此依赖关系作为图中的边)画出一张图,如下

在这里插入图片描述

注意:此图的顶点代表的是活动,边代表的是活动之间的依赖关系,所以此图也叫做AOV网

AVO网和DAG的关系:DAG一定是AOV网,但是AOV网不一定是DAG

2.计算图中所有顶点的入度存入 到一个数组中去
3.申请一个队列
4.将数组中所有入度为0的顶点放入到队列中,将位于队列中的首位的顶点弹出,将依赖于它的顶点的入度减1(更新数组)直到队列中没有顶点了继续,重复操作4直到数组中没有入度为0的顶点结束操作
问题:拓扑排序是如何检测它所服务的结构是不是DAG的

看拓扑序列中的顶点总数是不是图中的顶点总数相同,

如果是那么它所服务的结构就是DAG

如果不是那么它所服务的结构就不是DAG

六.二部图(二分图)

1.二部图的定义

G=(U,V,E)

U和V是两个顶点的集合(U和V各自内部是没有边关系的,U和V这两个集合之间有边关系),E是产生在U和V之间的边的集合

2.观察下面的两张图判断是否是二分图

第一张图如下

在这里插入图片描述

此图不是二部图

看下图进行理解

在这里插入图片描述

我们将此图中的顶点进行阵营的划分,对号为一个顶点的集合(第一个阵营),圆圈为另一个顶点的集合(第二个阵营),我们发现此图进行划分阵营后,相同阵营的顶点之间会有连接,所以此图不是二部图

第二张图如下

此图是二部图

看下图进行理解

在这里插入图片描述

我们将此图中的顶点进行阵营的划分,对号为一个顶点的集合(第一个阵营),圆圈为另一个顶点的集合(第二个阵营)可以发现相同阵营的顶点之间是没有连接的,所以此图是二部图

提示:是否是二部图的判断用的是BFS,(上面两个图的判断是不是二部图用的就是BFS)

3.将上面第二张图(也就是满足二部图的图)变成二部图如下

在这里插入图片描述

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

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

相关文章

阿里云RDS关系型数据库详细介绍_多版本数据库说明

阿里云RDS关系型数据库大全&#xff0c;关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等&#xff0c;NoSQL数据库如Redis、Tair、Lindorm和MongoDB&#xff0c;阿里云百科分享阿里云RDS关系型数据库大全&#xff1a; 目录 阿里云RDS关系型数据库大全 …

Springboot——jxls实现同sheet多个列表展示

文章目录 前言制定模板1、限定模板数据的范围2、设定报表展示项 编写测试类1、将xls模板文件放于 resource 下的 doc文件夹中2、导入依赖文件3、编写接口和导出逻辑 效果预览结论 前言 在之前的博客中Springboot——使用jxls实现excel模板导出excel&#xff0c;具体说明了jxls…

Springboot 音乐网站管理系统idea开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 音乐网站管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统 具有完整的源代码和数据库&…

数据结构刷题训练——二叉树篇(一)

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…

【已解决】Python打包文件执行报错:ModuleNotFoundError: No module named ‘pymssql‘

【已解决】Python打包文件执行报错&#xff1a;ModuleNotFoundError: No module named pymssql 1、问题2、原因3、解决 1、问题 今天打包一个 tkinter pymssql 的项目的时候&#xff0c;打包过程很顺利&#xff0c;但是打开软件的时候&#xff0c;报错 ModuleNotFoundError: …

使用企业订货系统后的效果|软件定制开发|APP小程序搭建

使用企业订货系统后的效果|软件定制开发|APP小程序搭建 企业订货系统是一种高效的采购管理系统&#xff0c;它可以帮助企业更好地管理采购流程&#xff0c;降低采购成本&#xff0c;提高采购效率。 可以帮助企业提高销售效率和降低成本的软件工具。使用该系统后&#xff0c;企业…

ctfshow-web4(文件包含日志注入)

像web3那样使用php伪协议&#xff0c;回显error 看了看提示&#xff1a;日志注入 文件包含 使用Wappalyzer查看一下&#xff0c;使用的中间件是Ngnix 日志包含漏洞的成因还是服务器没有进行严格的过滤 &#xff0c;导致用户可以进行任意文件读取&#xff0c; 但是前提是服务器…

百元开放式耳机哪款好一点耐用、百元耳放推荐

在耳机品类中&#xff0c;佩戴无需入耳、可保持耳道舒适的开放式耳机正成为新的潮流&#xff0c;不仅不少消费者趋之若鹜&#xff0c;相对于传统入耳式耳机&#xff0c;开放式耳机具备开放双耳的特性&#xff0c;能敞开耳道&#xff0c;让耳朵随时呼吸&#xff0c;保持干燥透气…

零基础Linux_13(基础IO_文件)文件系统接口+文件描述符fd+dup2函数

目录 1. C语言的文件操作 1.1 C语言文件的写入 1.2 当前路径 1.3 文件操作模式 1.4 文件的读取和cat 2. 文件系统接口 2.1 系统调用与封装 2.2 open打开文件 2.2.1 flags标记位 2.2.2 open用法演示 2.3 close关闭文件和write写入文件和rede读取文件 2.3.1 O_TRUNC…

攻防世界-T1 Training-WWW-Robots

文章目录 步骤1步骤二结束语 步骤1 看到文本——>提取有效信息——>利用有效信息 文本&#xff1a;In this little training challenge, you are going to learn about the Robots_exclusion_standard. The robots.txt file is used by web crawlers to check if they …

C++——多态底层原理

虚函数表 先来看这个问题&#xff1a; class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b 1; }; sizeof(Base)是多少&#xff1f; 答案是&#xff1a;8 因为Base中除了成员变量_b,还有一个虚函数表_vfp…

关于对XSS原理分析与绕过总结

一、原理 该文章仅用于信息防御技术教学&#xff0c;请勿用于其他用途。 1、XSS原理 XSS&#xff08;跨站脚本攻击&#xff09;是一种常见的网络安全漏洞&#xff0c;攻击者通常会在网页中插入恶意的 JavaScript 代码。由于服务器对输入数据的过滤和验证不严格&#xff0c;这…