图论相关算法理解和总结
晚上学习了一些图论相关算法:
单源最短路径算法:
Bellman-Ford 算法:
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法。该算法由 Richard Bellman 和 Lester Ford 分别发表于 1958 年和 1956 年,而实际上 Edward F. Moore 也在 1957 年发布了相同的算法,因此,此算法也常被称为 Bellman-Ford-Moore 算法。
Bellman-Ford 算法和 Dijkstra 算法同为解决单源最短路径的算法。对于带权有向图 G = (V, E),Dijkstra 算法要求图 G 中边的权值均为非负,而 Bellman-Ford 算法能适应一般的情况(即存在负权边的情况)。一个实现的很好的 Dijkstra 算法比 Bellman-Ford 算法的运行时间要低。
Bellman-Ford 算法采用动态规划(Dynamic Programming)进行设计,实现的时间复杂度为 O(V*E),其中 V 为顶点数量,E 为边的数量。Dijkstra 算法采用贪心算法(Greedy Algorithm)范式进行设计,普通实现的时间复杂度为 O(V2),若基于 Fibonacci heap 的最小优先队列实现版本则时间复杂度为 O(E + VlogV)。
Bellman-Ford 算法描述:
创建源顶点 v 到图中所有顶点的距离的集合 distSet,为图中的所有顶点指定一个距离值,初始均为 Infinite,源顶点距离为 0;
计算最短路径,执行 V - 1 次遍历;
对于图中的每条边:如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d;
检测图中是否有负权边形成了环,遍历图中的所有边,计算 u 至 v 的距离,如果对于 v 存在更小的距离,则说明存在环;
代码:
1 //从顶点from指向顶点to的权值为cost的边 2 struct edge{ 3 int from,to,cost; 4 }; 5 6 edge es[MAX_V];//边 7 8 int d[MAX_V]; //最短距离 9 int V,E; //V是顶点数,E是边数 10 11 //求解从顶点s出发到所有点的最短距离 12 void shortest_path(int s) 13 { 14 for(int i=0; i<V; i++) 15 d[i] = INF; //0x3f3f3f3f 16 d[s]=0; 17 while(true){ 18 bool update=false; 19 for(int i=0; i<E; i++){ 20 edge e=es[i]; 21 if(d[e.from]!=INF && d[e.to] >d[e.from]+e.cost){ 22 uopdate=true; 23 } 24 } 25 if(!update) 26 break; 27 } 28 }
Dijkstra算法
1.定义概览
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)
2.算法描述
1)算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
2)算法步骤:
a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。
b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
d.重复步骤b和c直到所有顶点都包含在S中。
执行动画过程如下图
STL代码:
1 struct edge{int to, cost;};//图的边 2 typedef pair<int,int> P;//保存的结果,first为最短距离,second为相应顶点 3 4 int V; 5 vector<edge> G[MAX_V]; 6 int d[MAX_V]; 7 8 void dijkstra(int s){ 9 //通过制定greater<P>参数,堆按照first从小到大的顺序取出值 10 priority_queue<P,vector<P>,greater<P>> que; 11 fill(d,d+V,INF); 12 d[s]=0; 13 que.push(P(0,s)); 14 15 while(!que.empty()){ 16 P p=que.top(); que.pop(); 17 int v=p.second; 18 for(int i=0;i<G[v].size;i++){ 19 edge e=G[v][i]; 20 if(d[e.to]>d[v]+e.cost){ 21 d[e.to]=d[v]+e.cost; 22 que.push(P(d[e.to],e.to)); 23 } 24 } 25 } 26 }
代码实现:
1 #define INF 0x3f3f3f3f 2 #define MAX 101 3 4 int dis[MAX],vis[MAX]; 5 int mp[MAX][MAX]; 6 7 int dijkstra(int s,int e) 8 { 9 memset(vis,0,sizeof(vis)); 10 for(int i=1; i<=e; i++) 11 dis[i]=mp[s][i]; 12 dis[s]=0; 13 vis[s]=1; 14 while(true){ 15 int min=INF; 16 int p; 17 for(int i=1; i<=e; i++){ 18 if(!vis[i] && dis[i]<min){ 19 min=dis[i]; 20 p=i; 21 } 22 } 23 if(min==INF) 24 break; 25 vis[p]=1; 26 for(int i=1; i<=e; i++){ 27 if(!vis[i] && dis[i]>min+mp[p][i]) 28 dis[i]=min+mp[p][i]; 29 } 30 } 31 }
SPFA:
是一种求单源最短路的算法
几乎所有的最短路算法其步骤都可以分为两步
1.初始化
2.松弛操作
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
1 int spfa(int s) 2 { 3 queue<int> q; 4 while(!q.empty()) 5 q.pop(); 6 q.push(s); 7 dis[s]=1.0; 8 vis[s]=1; 9 num[s]++; 10 while(!q.empty()){ 11 s=q.front(); 12 q.pop(); 13 vis[s]=0; 14 for(int i=0; i<list[s].size(); i++){ 15 int p=list[s][i]; 16 if(dis[p]<dis[s]*mp[s][p]){ 17 dis[p]=dis[s]*mp[s][p]; 18 if(!vis[p]){ 19 vis[p]=1; 20 q.push(p); 21 num[p]++; 22 if(num[p]==n) 23 return 0; 24 } 25 } 26 } 27 } 28 return 1; 29 }
1 int spfa_bfs(int s) 2 { 3 queue <int> q; 4 memset(d,0x3f,sizeof(d)); 5 d[s]=0; 6 memset(c,0,sizeof(c)); 7 memset(vis,0,sizeof(vis)); 8 9 q.push(s); vis[s]=1; c[s]=1; 10 //顶点入队vis要做标记,另外要统计顶点的入队次数 11 int OK=1; 12 while(!q.empty()) 13 { 14 int x; 15 x=q.front(); q.pop(); vis[x]=0; 16 //队头元素出队,并且消除标记 17 for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表 18 { 19 int y=v[k]; 20 if( d[x]+w[k] < d[y]) 21 { 22 d[y]=d[x]+w[k]; //松弛 23 if(!vis[y]) //顶点y不在队内 24 { 25 vis[y]=1; //标记 26 c[y]++; //统计次数 27 q.push(y); //入队 28 if(c[y]>NN) //超过入队次数上限,说明有负环 29 return OK=0; 30 } 31 } 32 } 33 } 34 35 return OK; 36 37 }
求多源、无负权边的最短路:
Floyd算法
1.定义概览
Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
2.算法描述
1)算法思想原理:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
2).算法描述:
a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
3).Floyd算法过程矩阵的计算----十字交叉法
代码:
1 int d[MAX_V][MAX_V]; //d[u][v] 表示边e=(u,v)的权值(不存在时设为INF,不过d[i][i]=0) 2 int v; 3 4 void warshall_floyd(){ 5 for(int k=0; k<V; k++) 6 for(int i=0; i<V; i++) 7 for(int j=0; j<V; j++) 8 d[i][j]=min(d[i][j],d[i][k]+d[k][j]); 9 }
最小生成树:
Prim算法
1.概览
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这课树就叫做生成树(Spanning Tree).如果边上有权值,那么是的边权和最小的生成树叫做最小生成树(MST,Minimum Spanning Tree)
2.算法简单描述
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
代码:
1 void prim() 2 { 3 memset(vis,0,sizeof(vis)); 4 memset(dis,INF,sizeof(dis)); 5 dis[1]=0; 6 ans=0; 7 dis[0]=INF; 8 while(true){ 9 int m=0; 10 for(int i=1; i<=n; i++){ 11 if(!vis[i] && dis[i]<dis[m]) 12 m=i; 13 } 14 if(m==0) 15 break; 16 vis[m]=1; 17 ans+=dis[m]; 18 for(int i=1; i<=n; i++) 19 dis[i]=min(dis[i],mp[m][i]); 20 } 21 }
Kruskal算法
1.概览
Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
2.算法简单描述
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
1 struct edge{int u,v,cost;}; 2 3 bool cmp(edge &e1,const edge &e2){ 4 return e1.cost < e2.cost; 5 } 6 7 edge es[MAX_E]; 8 int V,E; //顶点数和边数 9 10 int kruskal(){ 11 sort(es,es+E,cmp); //按照edge.cost的顺序从小到大排列 12 init_union_find(V); //并查集的初始化 13 int res=0; 14 for(int i=0; i<E; i++){ 15 edge e=es[i]; 16 if(!same(e.u,e.v)){ 17 unite(e.u,e.v); 18 res+=e.cost; 19 } 20 } 21 return res; 22 }
图论所刷题目的链接: http://www.cnblogs.com/qscqesze/p/4547000.html
参考书籍:<<挑战程序设计竞赛>>
参考博客:http://blog.csdn.net/yutianzuijin/article/details/11618651
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html
http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html
http://blog.csdn.net/v_july_v/article/details/6181485
转载于:https://www.cnblogs.com/wangmengmeng/p/5277225.html
图论相关算法理解和总结相关推荐
- svd文章分类 c语言,推荐系统相关算法:SVD
假如要预测Zero君对一部电影M的评分,而手上只有Zero君对若干部电影的评分和风炎君对若干部电影的评分(包含M的评分).那么能预测出Zero君对M的评分吗?答案显然是能.最简单的方法就是直接将预测分 ...
- 活动报名 | 北京交通大学魏云超:连续学习下像素理解的相关算法介绍
活动议程 日期:11月24日(周四) 时间 主题 14:30-14:35 开场简介 穆亚东 北京大学研究员.长聘副教授.博士生导师.北大博雅青年学者,青源会会员 14:35-15:20 连续学习下像 ...
- Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现
Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...
- 15-垃圾回收相关算法
垃圾回收相关算法 标记阶段:引用计数算法(Java未使用) 垃圾标记阶段:对象存活判断 在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死 ...
- JVM学习笔记之-拉圾回收概述,垃圾回收相关算法
拉圾回收概述 什么是垃圾 垃圾收集,不是Java语言的伴生产物.早在1960年,第一门开始使用内存动态分配和垃圾收集技术的Lisp语言诞生. 关于垃圾收集有三个经典问题: 哪些内存需要回收? 什么时候 ...
- 聚类分析与相关算法(Kmeans等)详解
聚类分析与相关算法详解 1 距离度量 1.1欧几里得距离定义 1.2其他距离度量的补充 1.3余弦距离 1.4简单匹配系数 1.5jaccard系数 1.6海明距离 2 聚类问题 2.1非监督学习中的 ...
- 算法基础:图的相关算法知识笔记
一.图的相关算法 1.图的分类知识 如下图: 2.生成树概念 对连通图进行遍历,过程中所经过的边和顶点的组合可看做是一棵普通树,通常称为生成树. 连通图的生成树具有这样的特征:边的数量 = 顶点数 - ...
- 线性表之链式存储结构_单链表相关算法
在存储结构上,不需要连续的存储空间,需要上一个结点的指针域 指向下一个结点即可,找到一个结点就可以找到下一个结点. 学习教材是大话数据结构,加上自己的一些个人理解.这个算法 有点绕,需要对指针 相关内 ...
- 不再惧怕!二叉树结构相关算法总结 | 原力计划
作者 | BoCong-Deng 来源 | CSDN 博客,责编 | 夕颜 出品 | CSDN(ID:CSDNnews) 写在前面 树结构对于程序员来说应该不陌生,特别是二叉树,基本只要接触算法这一类 ...
最新文章
- jQuery.append()、jQuery.html()存在的XSS漏洞
- Windows 文件过滤驱动经验总结
- Flutter学习指南:文件、存储和网络
- Postgresql在CentOS上的安装(脚本在线安装)
- 教你认识H3C的设备
- ListView条目中有CheckBox点击事件失效问题
- 美团暑期实习前端面试
- 智能运维 devops_Coffee Shop DevOps:如何使用反馈循环变得更智能
- 深度优先搜索之踩方格问题
- bert获得词向量_只需几行 Python 代码,即可用 BERT 玩转词嵌入!
- redis用HyperLogLog计算UV
- gcc -o sqlite3 shell.c sqlite3.c -ldl -lpthread
- 【王道计组笔记】定点数编码方式(原码,补码,反码)
- Linux下安装gcc环境
- CLRC663学习笔记
- window 搜索占用进程 资源监视器
- Linux 创建.sh脚本文件
- 删除c盘itunes备份_如何查找,备份和删除iTunes备份
- 前端页面适应不同分辨率
- Bartender安装时间长,安装成功后未跳出激活界面的解决方法,亲测2019和2021版