考研图论算法

news/2024/4/28 5:13:38/文章来源:https://blog.csdn.net/okok__TXF/article/details/127424292

图论——txf

倘若考研需要像写算法题目那样,写出图论的代码,那无疑图论是最难级别的。 -----Williams Tian

1. 重点表述

①线形表可以空表,树可以空树,但是图不能一个顶点也没有(允许一条边也没有).

②注意“无向图” 和 “有向图” 中一些表述的不同

顶点个数n,边为m
1. 有向图,无向图都可以说的
完全图、简单完全图:(图中任意两点,可以直接直接直接到达)
(无向图)n个点最多有 (n(n-1))/2 条边;
(有向图)n个点最多有 (n(n-1)) 条边;

思考:无向图9个点,至少需要多少条边才能确保其实一个连通图?

2. 连通图、连通分量一般用于无向图中,对于有向图的话,多一个“强”字

**连通图:**任意两点都可以到达,注意是到达,不是直接到达

极大连通子图:又被称为连通分量

**极小连通子图:**注意与极大连通子图区分!(并不一定包含图的所有顶点,同极大连通子图)

生成树:包含图的所有顶点,并且连接这些点使之是一个连通图 所需要的边最少

在这里插入图片描述

概念解析题:

在这里插入图片描述

G’是生成树,仔细想想生成树包含子图的所有边吗?而连通分量包含子图所有边,故I错

然后根据生成树的概念可知2 3 对 此题选D

③ 关于图的表示

1.邻接矩阵:遍历–>普遍选O(n^2)时间复杂度

2.邻接表:遍历–>普遍选O(n+e)时间复杂度

3.邻接多重表(无向图),十字链表(有向图)--------记忆:室友(“室”字链表-----“友”向图)

在这里插入图片描述

④图的遍历

bfs:邻接矩阵时间复杂度O(n^2),空间复杂度O(n);邻接表时间O(n+e),空间O(n)

dfs:邻接矩阵时间复杂度O(n^2),空间复杂度O(n);邻接表时间O(n+e),空间O(n)

判断回路确切的两个方法 1.拓扑排序 2.dfs遍历 有争议的方法:关键路径

2. 代码论述

对于考研题目来说,不一定要运行能够跑通啊,只是大致完整的思路,(比较规范的伪代码)

①图的表示

邻接矩阵:

typedef struct {VertType vert[MAXN]; //表示顶点的信息EdgeType edge[MAXN][MAXN]; //二维数组   int n, m;  //顶点数,边数
}MGraph;

邻接表

typedef struct ArcNode{ //边表结点int index; //指向的点所在的位置struct ArcNode *next;
}ArcNode;typedef struct VNode{ //顶点表信息VertType data; //顶点信息ArcNode *first; //顶点表的第一条弧
}VNode; typedef struct {Vnode vertTable[MAXN]; //邻接表int n, m; //顶点,边数
}ALGraph; //邻接表所表示的图

可能看着会有些复杂,我画一个图就理解了;(边无权值)

思考:如果边有权值,该怎么表示?

在这里插入图片描述

邻接表,邻接矩阵二者互相转化

//邻接表转为邻接矩阵
void table_TO_matrix(ALGraph *G, int matrix[N][N]) {for ( int i = 0; i < G->n; ++i ) { //遍历顶点表ArcNode *p = G->vertTable[i].first; //p就定位到第一个边结点了while ( p != null ) {matrix[i][p->index] = 1;p = p->next;}}
}//邻接矩阵转为邻接表(稍微有些复杂哦)
void matrix_TO_table(int MGraph *G, VNode vertTable[N]) {//将顶点信息 先复制下来for ( int i = 0; i < G->n; ++i ) {vertTable[i] = G->vert[i];}//组织边的关系for ( int i = 0; i < G->n; ++i ) {ArcNode *p;bool sign = false;for ( int j = 0; j < G->n; ++j ) {if ( G->edge[i][j] == 1 ) {ArcNode *t = new ArcNode;t->index = j;t->next = null;if ( !sign) {vertTable[i].first = t;p = t;}else{p->next = t;p =t;}sign = true;}}}}

② 图的遍历

bfs:

//需要借助一个队列
//1.基于邻接矩阵的bfs
bool visted[MAXN] = {false};
void visit( node ); //表示访问此点
void Bfs(Graph *G, int start) {Queue q = InitQueue(); //初始化一个队列q.push(start);while ( !q.empty() ) {top = q.top(); //获取队头q.pop(); //删除队首元素visit(top); //访问此元素visited[top] = true;for ( int i = 0; i < G->n; ++i ) {if ( G->edge[top][i] == 1 && !visited[i] ) {q.push(i);}}}
}
//2.基于邻接表的bfs
bool visted[MAXN] = {false};
void Bfs(Graph *G, int start) {Queue q = InitQueue(); //初始化一个队列q.push(start);while ( !q.empty() ) {top = q.top(); //获取队头q.pop(); //删除队首元素visit(top); //访问此元素visited[top] = true;ArcNode *p = G->vertTable[top].first;while ( p != null ) {if ( !visited[p->index] ) q.push(p->index);p = p->next;}}
}

dfs

//1.基于邻接矩阵的dfs,递归
bool visited[MAXN] = {false};
void Dfs( Graph *G, int start ) {visit(start);visited[start] = true;for ( int i = 0; i < G->n; ++i ) {if ( G[start][i] == 1 && !visited[i] ) {Dfs(G, i);}}
}//2.基于邻接表的dfs,递归
bool visited[MAXN] = {false};
void Dfs( Graph *G, int start ) {visit(start);visited[start] = true;ArcNode *p = G->vertTable[start].first;while ( p != null ) {if ( !visited[p->index] ) {Dfs(G, p->index);}p = p->next;}}//3.基于邻接表的dfs,非递归,需要一个栈了
bool visited[MAXN] = {false};
void Dfs( Graph *G, int start ) {Stack s = InitStack(); //初始化一个栈s.push(start);visited[start] = true;while ( !s.empty() ) {top = s.top(); //获取栈顶元素s.pop(); //弹出栈顶元素visit(top); //访问此元素ArcNode *p = G->vertTable[top].first;while ( p != null ) {if ( !visited[p->index] ) {s.push(p->index);visited[p->index] = true;}}}}//1 3方法遍历顺序有区别!!请思考一下。  但是都是深度优先的模式

③生成树算法

1.prim算法

算法模型:

void Prim( G, T ) { //G表示图,T表示生成树T = NULL; //空树T.put(start); // 将start(任意一节点)放入生成树中while ( T.number != G.n ) { //T的顶点个数 不等于 G图的顶点个数Vert t = Find_Min_Go_T();//在G中非生成树的点中,寻找距离生成树最近的点T.put(t); //将此点放入生成树中UpDateDis(t); //用此点更新其余点到生成树的最小距离}
}

伪代码(邻接矩阵表示)

/*
edge[i][j] = k //表示 i到j 这条边距离是 k
*/
const int INF = 0x3f3f3f3f; //表示无穷大
void Prim( Graph G ) {int TreeTotal = 0; //生成树的顶点个数bool tree[G.n] = {false};int dis[G.n] = { 0 }; //dis[i]表示点i到生成树的距离tree[0] = true; //从0顶点开始,此点加入生成树TreeTotal++;for ( int i = 0; i < G.n; ++i ) {dis[i] = G.edge[0][i];}while ( TreeTotal < G.n ) {//找距离生成树最近的点int mins = INF, t = -1;for ( int i = 0; i < G.n; ++i ) {//如果没有在生成树里面if ( !tree[i] && mins > dis[i] ) {mins = dis[i];t = i;}}//以上步骤找到了最近的点tif ( t != -1 ) {TreeTotal++; //放入生成树中,此时这条边其实也找出来了,也可以想个办法把边也加入生成树tree[t] = true;//用此点更新其他点到生成树的距离for ( int i = 0; i < G.n; ++i ) {//t到i有边if (  !tree[i] && G.edge[t][i] ) {if ( dis[i] > G.edge[t][i] ) dis[i] = G.edge[t][i];}}}  }//结束了
}

此种算法是O(n^2), 时间复杂度,可以优化,但是我这里再讲就超纲了。

2.kruskal算法

算法模型:

void Kruskal( G, T ) {int numS = G.n; //刚开始连通分量为nwhile ( numS > 1 ) {//从 边的集合 中按照权值大小从小到大依次寻找edge = findMinEdge(); //找到了最小的边if ( 边的起点,终点 不在一个连通分量中 ) {//将边的起点,终点并入到一个集合中Merge(start, end);    numS--;}} //结束,最后只有一个连通分量了
}

伪代码(邻接矩阵)

涉及到了快速排序,并查集

typedef struct {int start, end, v; //此条边表示start -> end 距离为v
}Edge;//传进来一个边构成的数组,就是图了
void Kruskal ( Edge edge[], int n ) {//将edge数组中的元素,按v从小到大排序//思考一下,为什么?QuickSort( edge , a.v < b.v ); /*初始化并查集数组,f[i]表示i的父亲是f[i];例如f[1] = 5, f[5] = 2, f[3] = 2;可以得知1 2 3 5为同一个集合里面, 他们的"根"都是2*/int f[n];for ( int i = 0; i < n; ++i ) f[i] = i;//此数组表示第i条边是否选入生成树bool select[n] = {false};int numS = n; //初始连通分量为n,因为此时生成树个数为0while ( numS > 1 ) {int t;for ( t = 0; i < n; ++i ) {if ( !select[i] ) break; //选出了边}int start = edge[t].start;int end = edge[t].end;int fs = findRoot(start); //表示找start的 “根”!!int fe = findRoot(end);if ( fs != fe ) {f[fe] =  fs; //将它们的最高级祖先合并,那么start,end自然就合并了numS--;select[t] = true;}}//结束了
}
//此函数需要理解,建议结合上面的例子 f[1] = 5, f[5] = 2, f[3] = 2;
int findRoot(int f[], int x ) {if ( f[x] == x ) return x;return f[x] = findRoot(f[x]);
}

思考:分析一下此算法的时间复杂度?

对比Prim算法,请自行理解 哪个算法适合稠密图,那个适合稀疏图。对于稠密图,稀疏图的严格区分,无绝对标准,只有一个

相对的判别: 边数E,顶点数V ==> E < VlogV稀疏

④最短路径算法

1.dijkstra算法 (单源最短路径)

算法模型略了,有点类似于prim

每次都从dis数组中找未选中的最近的点,用它来更新dis数组

代码:

//求start到各点的距离, INF表示无穷大
void Dijkstra(Graph G, int start) {int dis[G.n] = {0}; //此数组表示strat点到i点的最近距离bool sign[G.n] = {false}; //i点选中了没有//初始化dis数组for ( int i = 0; i < G.n; ++i ) {if ( i == start ) dis[i] = 0;else dis[i] = INF;}sign[start] = true;for ( int i = 0; i < G.n; ++i ) {//strat到i可以直接到达if ( G.edge[start][i] != INF ) dis[i] = G.edge[start][i];}//=======================================================//这中间才是核心部分for ( int i = 0; i < G.n; ++i ) {//从dis数组中选出距离strat最近的点,并且此点未被选中int mins = INF, t = -1;for ( int j = 0; j < G.n; ++j  ) {if ( !sign[j] && mins > dis[j] ) {mins = dis[j]; t = j;}}if ( t == -1 ) break; //找了一轮没找到,说明点选中完了sign[t] = true; //选中t//把t作为中转点,看可不可以strat->j 通过t中转变得更小 start -> t -> jfor ( int j = 0; j < G.n; ++j ) {if ( dis[j] > dis[t] + G.edge[t][j] ) {dis[j] = dis[t] + G.edge[t][j];}}}//=======================================================//结束for ( int i = 0; i < G.n; ++i ) {cout << start << "-->" << i << " = ";if ( dis[i] == INF ) cout << "∞" << endl;else cout << dis[i] << endl;}cout << endl;
}

测试数据图片(参考自别人的)
在这里插入图片描述

可以运行的测试代码

#include <iostream>
#include <cstdio>#define MAX 100
#define INF 0x3f3f3f3f
using namespace std;/* 测试数据10 13
0 1 10
0 2 15
1 3 20
1 4 5
2 5 8
2 6 6
3 7 7
4 5 10
4 7 3
4 8 4
5 6 9
5 8 3
6 8 12
*/int path[105];//打印路径用的
typedef struct {int edge[MAX][MAX];//邻接矩阵,记录的是两点之间的距离,也就是权值int n,m;//边数和顶点数
}Graph;void Dijkstra(Graph G, int start);
void printPath( int u, int x, int dis[]); //打印从u到x的路径//主函数
int main()
{Graph g;int n, m;cin >> n >> m;for ( int i = 0; i < n; ++i ) {for ( int j = 0; j < n; ++j ) {g.edge[i][j] = INF;}}for ( int i = 0; i < m; ++i ) {int s, e, v;cin >> s >> e >> v;g.edge[s][e] = v;g.edge[e][s] = v;}g.n = n; g.m = m;Dijkstra(g, 0);return 0;
}//求start到各点的距离, INF表示无穷大
void Dijkstra(Graph G, int start) {int dis[G.n] = {0}; //此数组表示strat点到i点的最近距离bool sign[G.n] = {false}; //i点选中了没有//初始化dis数组for ( int i = 0; i < G.n; ++i ) {if ( i == start ) dis[i] = 0;else dis[i] = INF;}sign[start] = true;for ( int i = 0; i < G.n; ++i ) {//strat到i可以直接到达if ( G.edge[start][i] != INF ) {dis[i] = G.edge[start][i];path[i] = start;}}for ( int i = 0; i < G.n; ++i ) {//从dis数组中选出距离strat最近的点,并且此点未被选中int mins = INF, t = -1;for ( int j = 0; j < G.n; ++j  ) {if ( !sign[j] && mins > dis[j] ) {mins = dis[j]; t = j;}}if ( t == -1 ) break; //找了一轮没找到,说明点选中完了sign[t] = true; //选中t//把t作为中转点,看可不可以strat->j 通过t中转变得更小 start -> t -> jfor ( int j = 0; j < G.n; ++j ) {if ( dis[j] > dis[t] + G.edge[t][j] ) {dis[j] = dis[t] + G.edge[t][j];path[j] = t;}}}//结束for ( int i = 0; i < G.n; ++i ) {cout << start << "-->" << i << " = ";if ( dis[i] == INF ) cout << "∞" << endl;else cout << dis[i] << endl;}cout << endl;for ( int i = 0; i < G.n; ++i ) {printPath(start, i, dis);}
}void printPath( int u, int x, int dis[]) {int a[MAX], cou = 0, ex = x;if( u == x )printf("%d --> %d = 0", u, x);else if( dis[x] == INF )printf("%d --> %d = ∞", u, x);//没有路径else{while( x != u) {a[cou++] = x;x = path[x];}a[cou] = x;for(int i = cou; i > 0; i--)printf("%d-->",a[i]);printf("%d", a[0]);}cout << endl;
}

总结:dijkstra三步法

1.初始化数据,dis数组,sign数组

2.从起点更新一下dis数组

3.for循环核心内容

2.Floyd算法 (多源最短路径)

算法模型;三重循环

for ( int k = 0; k < n; ++k ) {for ( int i = 0; i < n; ++i ) {for ( int j = 0; j < n; ++j ) {/*i->j的距离 为s1i->k->j的距离为(i通过k中转,然后去到j) s2如果s1 > s2, 那么就把短的赋值给edge[i][j]*/if ( G.edge[i][j] > G.edge[i][k] + G.edge[k][j] ) {G.edge[i][j] = G.edge[i][k] + G.edge[k][j]}}    }   
}

注意事项:最外层循环必须是中转点

无了

3. 关键路径

在这里插入图片描述

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

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

相关文章

ETC-4 week 3th

ETC-4 week 3th 出奇至胜 read They are only charged for the amount of power they consume on rainy days.They needn’t pay a single cent for their power consumption(消耗能量) on sunny days.(13 june) consume v 消耗 耗尽 吃光 喝光 沉溺 浪费LOL consumes(消耗…

安装docker,打包jar包镜像文件,输出tar压缩包

打包 jar 步骤在文章最后&#xff0c;不需要安装的请直接跳到文末查看 一键安装命令&#xff1a; curl -sSL https://get.daocloud.io/docker | sh设置开机自启并启动docker systemctl enable docker.service启动docker systemctl start docker查看docker状态 systemctl s…

创新洞见|2023年B2B业务为何必须采用PLG增长策略

随着采用PLG模式的大型企业数量不断增加&#xff0c;91%的公司计划在2022年增加对PLG战略的投资&#xff0c;市场上已经验证了PLG公司的表现优于其竞争对手&#xff0c;规模增长更快&#xff0c;并拥有更高的企业价值&#xff08;EV&#xff09;。PLG象征着购买决策者的转变&am…

【附源码】计算机毕业设计SSM数据时代下的疫情管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java多线程之Thread和Runnable关于共享资源的对比

背景 Thread和Runnable关于共享资源的对比&#xff0c;网上看到很多不正确的结论如下&#xff1a; Thread类创建多线程&#xff0c;无法保证多个线程对共享资源的正确操作&#xff0c;而Runnable接口可以保证多个线程对共享资源的正确访问。 得到这个结论的原因如下&#xff1…

【Pytorch】learning notes

文章目录【torch.xxx】torch.addmm() / torch.addmm_()torch.clamp() / torch.clamp_()torch.eq() / torch.ne()torch.manual_seed()torch.unique()torch.save() / torch.load()torch.view() / torch.permute() / torch. transpose() / torch.reshape()【torch.cuda.xxx】torch…

可以替代911s5的这几款产品还有跨境人士不知道吗?

不久前跨境电商用户都收到的坏消息无疑就是&#xff1a;911s5正式宣布停止运营并永久关闭。对于911s5&#xff0c;相信几乎所有的跨境电商用户都知道&#xff0c;因为其低廉的价格一直很受欢迎。所以一时间大家纷纷寻找911s5的替代品&#xff0c;但不是那么容易找的。今天这篇文…

投资组合图形化:EAP.util.plot

实证资产定价&#xff08;Empirical asset pricing&#xff09;已经发布于Github和Pypi. 包的具体用法(Documentation)博主将会陆续在CSDN中详细介绍&#xff0c;也可以通过Pypi直接查看。 Pypi: pip install --upgrade EAP HomePage&#xff1a; EAP a catchy description …

38 字典名[键名]=值 向字典增加键值对

38 字典名[键名]值 向字典增加键值对 文章目录38 字典名[键名]值 向字典增加键值对1. 语法2. 代码示例1. 字典中有要操作的键名—作用为修改2. 字典中没有要操作的键名—作用是增加3. 课后练习4. 列表增加元素知识回顾5. 总结1. 语法 向字典中增加键值对和修改字典的值的语法结…

开箱即用的数据缓存服务|EMQX Cloud 影子服务应用场景解析

在物联网业务高速迭代的今天&#xff0c;快速连接物联网设备与平台应用&#xff0c;实现业务快速落地与市场验证&#xff0c;是很多企业塑造核心竞争力、实现业务创新的关键。 EMQX Cloud 作为一站式运维代管的 MQTT 消息云服务&#xff0c;可以帮助用户在公有云环境中快速实现…

JavaScript:模拟拍照

实现拍照功能需要使用电脑的摄像头&#xff0c;可以使用 navigator.mediaDevices.getUserMedia() 方法&#xff0c;传递相应的参数就能开启摄像头 navigator.mediaDevices 是一个媒体设备对象&#xff0c;通过 getUserMedia( )方法开启音频和视频媒体设备。 getUserMedia 参数…

文献阅读-融合注意力机制的 IETM 细粒度跨模态检索算法

引用格式&#xff1a;翟一琛&#xff0c;顾佼佼&#xff0c;宗富强&#xff0c;姜文志&#xff0e;融合注意力机制的 IETM 细粒度跨模态 检索算法[J/OL]&#xff0e;系统工程与电子技术. https://kns.cnki.net/kcms/detail/11.2422.TN.20220823.1030.004.html 期刊&#xff1a…

跟李沐学AI-动手学深度学习1

整体内容 神经网络可以理解为是一种语言 数学和代码的结合&#xff0c;道术结合&#xff0c;关键在动手 是什么&#xff0c;怎么做&#xff0c;为什么这样 发展知识和应用 广告点击预测三个步骤 预测和训练 模型控制广告展现 数据格式 0维&#xff0c;1维&#xff0c…

【仿牛客网笔记】初识Spring Boot,开发社区首页-MyBatis入门

安装MySQL Server 安装MySQL Workbench 安装过程略。。。 Mybatis手册 Mybatis整合 Mybatis的核心组件&#xff1a; SqlSessionFactory:用于创建SqlSessionFactory工厂类。 SqlSession&#xff1a;Mybatis的核心组件&#xff0c;用于数据库执行SQL 主配置文件&#xff1a;XM…

大一学生期末大作业 html+css+javascript网页设计实例【电影购票项目】html网页制作成品代码

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

java面试题总结-1

Java语言特点 &#xff08;1&#xff09;简单易学、有丰富的类库 &#xff08;2&#xff09;面向对象&#xff08;java最重要的特性&#xff0c;让程序耦合度更低&#xff0c;内聚性更高&#xff09; &#xff08;3&#xff09;与平台无关性&#xff08;JVM是Java跨平台使用的…

拦截器和过滤器

拦截器和过滤器 参考&#xff1a; 过滤器和拦截器的区别_至今没搞明白的博客-CSDN博客_过滤器和拦截器的区别 拦截器与过滤器的区别_℡tang的博客-CSDN博客_拦截器和过滤器的区别 文章目录拦截器和过滤器过滤器概念作用Filter链与Filter生命周期SpringBoot 实现过滤器方式一…

如何将各大网盘整合到一起顺便挂载本地使用(文末附软件获取方式)

目录 1、Alist.exe 2、RaiDrive 今天发现了一个网盘变硬盘神器&#xff0c;它不仅安全免费&#xff0c;更全面支持&#xff1a;百度网盘、阿里云盘、天翼云盘、蓝奏云、闪电盘、夸克网盘、迅雷网盘、等众多你们听过&#xff0c;以及没有听过的所有网盘&#xff01; 直接先看效…

Mac环境下反编译工具的使用

日常工作中避免不了反编译工具经常安装&#xff08;换电脑设备、手滑把文件夹删除了。。。等等原因&#xff09;&#xff0c;而且时间一久忘记命令的使用&#xff0c;因此做下记录。 一、反编译工具三件套 apktool&#xff1a;获取apk里的资源文件、配置文件、清单文件、lib文…

毕业论文中引用方法、原理、定义等 如何降重才更有效果?

论文重复率过高是一件很痛苦的事&#xff0c;我当年的本科论文&#xff0c;一共查了四遍才过。 我的查重方法其实比较简单&#xff0c;初稿出来以后我就开始查重了&#xff0c;然后按照标注把标红的部分全部修改掉&#xff0c;而后以此类推&#xff0c;每次改外&#xff0c;或…