ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

图算法详解:最短路径、拓扑排序与关键路径

图算法详解:最短路径、拓扑排序与关键路径 图是数据结构中的重要概念在实际应用中有着广泛的用途。本文将深入探讨图的三个核心算法最短路径算法、拓扑排序和关键路径算法帮助读者理解其原理、实现和应用场景。文章目录一、最短路径算法1.1 Dijkstra算法 - 单源最短路径1.2 Floyd算法 - 全源最短路径二、拓扑排序算法2.1 基于邻接矩阵的实现2.2 基于邻接表的改进实现三、关键路径算法3.1 算法实现四、算法应用与总结4.1 应用场景对比4.2 算法选择建议4.3 性能优化提示一、最短路径算法最短路径问题是图论中的经典问题旨在寻找图中两点间路径权值和最小的路径。根据求解范围的不同可以分为单源最短路径和全源最短路径问题。1.1 Dijkstra算法 - 单源最短路径Dijkstra算法用于解决从某个源点到图中其他所有顶点的最短路径问题适用于非负权值的有向图或无向图。算法核心思想维护一个距离数组D[]记录源点到各顶点的最短距离使用贪心策略每次选择当前距离最小且未访问的顶点通过该顶点更新其邻接顶点的距离值floatD[n];// 存放各条最短路径的长度intp[n],s[n];// p[]记录前驱节点s[]标记是否已访问voidDijkstra(intv,floatdist[][n]){inti,j,k,v1,min,max10000;v1v;// 初始化for(i0;in;i){D[i]dist[v1][i];if(D[i]!max)p[i]v11;elsep[i]0;s[i]0;}s[v1]1;// 源点标记为已访问// 主循环找到n-1个最短路径for(i0;in-1;i){minmax1;// 找到距离最小的未访问顶点for(j0;jn;j){if((!s[j])(D[j]min)){minD[j];kj;}}s[k]1;// 标记为已访问// 更新通过顶点k可达的其他顶点的距离for(j0;jn;j){if((!s[j])D[j]D[k]dist[k][j]){D[j]D[k]dist[k][j];p[j]k1;}}}}复杂度分析时间复杂度O(n²)空间复杂度O(n)1.2 Floyd算法 - 全源最短路径Floyd算法能够求出图中任意两个顶点之间的最短路径采用动态规划的思想实现。算法核心思想通过一个中间顶点k判断经由k的路径是否比直接路径更短逐个尝试每个顶点作为中间节点更新所有顶点对之间的最短距离intpath[n][n];// 路径矩阵记录路径信息voidFloyd(floatA[][n],floatdist[][n]){inti,j,k,max1000;// 初始化路径长度矩阵和路径矩阵for(i0;in;i){for(j0;jn;j){if(dist[i][j]!max)path[i][j]i1;elsepath[i][j]0;A[i][j]dist[i][j];}}// 三重循环k为中间顶点for(k0;kn;k){for(i0;in;i){for(j0;jn;j){if(A[i][j]A[i][k]A[k][j]){A[i][j]A[i][k]A[k][j];// 更新距离path[i][j]path[k][j];// 更新路径}}}}}复杂度分析时间复杂度O(n³)空间复杂度O(n²)二、拓扑排序算法拓扑排序是针对**有向无环图DAG**的一种排序方法用于确定顶点的一个线性序列使得对于图中每条有向边(u,v)顶点u都出现在顶点v之前。应用场景课程安排先修课程关系项目调度编译器中的依赖分析2.1 基于邻接矩阵的实现voidTopoSortA(Graph*g,intn){inti,j,k,t,v,D[n];for(i0;in;i)D[i]0;// 标记数组初始化v1;// 序号计数器for(k0;kn;k){// 寻找入度为0的顶点全0列for(j0;jn;j){if(D[j]0){t1;for(i0;in;i){if(g-arcs[i][j]1){t0;break;}}if(t1){mj;break;}}}if(j!n){D[m]v;// 分配新序号printf(%d\t,g-vex[m]);for(i0;in;i)g-arcs[m][i]0;// 删除该顶点的所有出边v;}elsebreak;}if(vn)printf(\n图中存在环路\n);}2.2 基于邻接表的改进实现typedefstruct{intadjvex;// 邻接点structnode*next;}EdgeNode;typedefstruct{intvertex;// 顶点信息intid;// 入度EdgeNode*link;// 边表头指针}VexNode;voidTopoSortB(VexNode ga[]){inti,j,k,m0,top-1;EdgeNode*p;// 建立入度为0的顶点栈for(i0;in;i){if(ga[i].id0){ga[i].idtop;topi;}}while(top!-1){jtop;topga[top].id;// 出栈printf(%d\t,ga[j].vertex);m;pga[j].link;while(p){kp-adjvex;ga[k].id--;// 入度减1if(ga[k].id0){ga[k].idtop;topk;// 新的零入度顶点入栈}pp-next;}}if(mn)printf(\n图中存在环路\n);}复杂度对比邻接矩阵实现O(n³)邻接表实现O(n e)三、关键路径算法关键路径算法用于解决**AOE网络Activity On Edge**中的项目调度问题寻找决定整个项目完成时间的关键活动序列。核心概念事件用顶点表示代表项目中的某个状态活动用边表示代表需要消耗时间的任务关键路径从起点到终点的最长路径关键活动位于关键路径上的活动3.1 算法实现typedefstructnode{intadjvex;// 邻接点intdur;// 活动持续时间structnode*next;}EdgeNode;typedefstruct{charvertex;// 顶点信息intid;// 入度EdgeNode*link;// 边表头指针}VexNode;intCriticalPath(VexNode digl[]){inti,j,k,m;intfront-1,rear-1;// 队列指针inttpord[n],ve[n],vl[n];intl[maxsize],e[maxsize];EdgeNode*p;// 初始化事件最早发生时间for(i0;in;i)ve[i]0;// 拓扑排序计算ve[]for(i0;in;i){if(digl[i].id0)tpord[rear]i;}m0;while(front!rear){front;jtpord[front];m;pdigl[j].link;while(p){kp-adjvex;digl[k].id--;// 更新最早发生时间if(ve[j]p-durve[k])ve[k]ve[j]p-dur;if(digl[k].id0)tpord[rear]k;pp-next;}}if(mn){printf(AOE网络中存在环路\n);return0;}// 初始化事件最迟发生时间for(i0;in;i)vl[i]ve[n-1];// 按逆拓扑序列计算vl[]for(in-2;i0;i--){jtpord[i];pdigl[j].link;while(p){kp-adjvex;if((vl[k]-p-dur)vl[j])vl[j]vl[k]-p-dur;pp-next;}}// 计算活动的最早开始时间e[]和最迟开始时间l[]i0;for(j0;jn;j){pdigl[j].link;while(p){kp-adjvex;e[i]ve[j];l[i]vl[k]-p-dur;printf(活动%d,%d e%d l%d 松弛时间%d\t,digl[j].vertex,digl[k].vertex,e[i],l[i],l[i]-e[i]);if(l[i]e[i])printf(关键活动);printf(\n);pp-next;}}return1;}算法步骤通过拓扑排序计算事件的最早发生时间ve[]按逆拓扑序列计算事件的最迟发生时间vl[]计算每个活动的最早开始时间e[]和最迟开始时间l[]找出松弛时间为0的活动e[i] l[i]即关键活动复杂度分析时间复杂度O(n e)空间复杂度O(n e)四、算法应用与总结4.1 应用场景对比算法适用场景典型应用Dijkstra单源最短路径非负权值GPS导航网络路由Floyd全源最短路径允许负权值距离矩阵计算传递闭包拓扑排序有向无环图排序课程安排依赖分析关键路径项目调度优化工程管理资源分配4.2 算法选择建议图规模较小且需要全源最短路径选择Floyd算法图规模较大且只需单源最短路径选择Dijkstra算法存在负权边但无负权环使用Bellman-Ford算法Floyd的变种需要判断依赖关系或检测环路使用拓扑排序项目管理和时间优化使用关键路径算法4.3 性能优化提示Dijkstra算法优化使用优先队列堆可将时间复杂度降至O((ne)logn)拓扑排序优化邻接表存储比邻接矩阵更高效关键路径优化可以结合并行计算技术处理大规模项目网络这三个算法构成了图论算法的重要基础掌握它们不仅有助于理解图的性质更能为解决实际问题提供有力工具。在实际应用中需要根据具体问题的特点选择合适的算法并考虑数据规模和性能要求进行相应的优化。
返回列表