news 2026/1/14 21:38:00

C语言图论:最短路径算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言图论:最短路径算法

本文献给:
已掌握无向图基础,希望理解如何在带权图中找到两点间最短路径的C语言学习者。本文将系统讲解两种经典的最短路径算法。


你将学到:

  1. 最短路径问题的定义与核心概念
  2. Dijkstra算法:解决单源、非负权图的最短路径
  3. Bellman-Ford算法:处理单源、含负权边的最短路径
  4. 算法对比与实战应用


目录

  • 第一部分:问题定义与核心概念
    • 1. 什么是最短路径?
  • 第二部分:图的存储(带权图)
  • 第三部分:Dijkstra算法
  • 第四部分:Bellman-Ford算法
    • 1. 核心思想
    • 2. 算法步骤
    • 3. C语言实现
  • 第五部分:总结与对比
    • 算法对比表
    • 选择指南

第一部分:问题定义与核心概念

1. 什么是最短路径?

在带权无向图G = ( V , E , w ) G = (V, E, w)G=(V,E,w)中,w : E → R w: E \rightarrow \mathbb{R}w:ER为边权函数。从源点s ss到目标t tt的路径P = ( s , v 1 , . . . , v k , t ) P = (s, v_1, ..., v_k, t)P=(s,v1,...,vk,t)的长度为路径上所有边权之和:
d i s t ( P ) = ∑ i = 0 k w ( v i , v i + 1 ) dist(P) = \sum_{i=0}^{k} w(v_i, v_{i+1})dist(P)=i=0kw(vi,vi+1)
最短路径是所有s sst tt路径中长度最小的路径。


关键术语:

  • 单源最短路径:求从一个源点s ss到图中所有其他顶点的最短距离。
  • 权值:可正、可负(实际问题中常为非负,如距离、时间、成本)。
  • 松弛操作:最短路径算法的核心操作,通过考察一条边来更新当前的最短距离估计。

第二部分:图的存储(带权图)

在基础邻接矩阵/邻接表上,需要增加权值信息。

#defineMAX_V100#defineINF0x3f3f3f3f// 表示“无穷大”的一个较大数值// 邻接矩阵(带权)typedefstruct{intmatrix[MAX_V][MAX_V];// 存储权值,INF表示无边intvertex_count;}GraphMatrixWeighted;voidinit_graph_weighted(GraphMatrixWeighted*g,intn){g->vertex_count=n;for(inti=0;i<n;i++){for(intj=0;j<n;j++){g->matrix[i][j]=(i==j)?0:INF;// 自身距离为0}}}voidadd_edge_weighted(GraphMatrixWeighted*g,intu,intv,intweight){if(u>=g->vertex_count||v>=g->vertex_count)return;g->matrix[u][v]=weight;g->matrix[v][u]=weight;// 无向图}

第三部分:Dijkstra算法

1. 核心思想

贪心策略。维护一个集合S SS,包含已确定最短距离的顶点。每次从未确定的顶点中选择距离源点s ss最近的顶点u uu加入S SS,并用u uu松弛其所有邻居的距离。要求:所有边权非负

算法正确性依赖:当边权非负时,当前未确定顶点中距离最小的顶点,其距离不可能再被其他未确定的顶点更新减小。


2. 算法步骤(朴素O ( ∣ V ∣ 2 ) O(|V|^2)O(V2)实现)

  1. 初始化:dist[s] = 0dist[v] = INFv ≠ s),visited[v] = false
  2. 循环|V|次:
    a. 找到未访问顶点中dist最小的顶点u
    b. 标记u为已访问 (visited[u] = true)。
    c.松弛操作:对u的每个邻居v,如果dist[u] + w(u, v) < dist[v],则更新dist[v] = dist[u] + w(u, v)
  3. 循环结束,dist[]数组即为源点到各点的最短距离。

3. C语言实现

// Dijkstra算法 (邻接矩阵,朴素实现)voiddijkstra(GraphMatrixWeighted*g,intsrc,intdist[]){intvisited[MAX_V]={0};// 初始化for(inti=0;i<g->vertex_count;i++){dist[i]=INF;}dist[src]=0;for(intcount=0;count<g->vertex_count;count++){// 步骤1: 找到未访问的dist最小顶点intu=-1;intmin_dist=INF;for(intv=0;v<g->vertex_count;v++){if(!visited[v]&&dist[v]<min_dist){min_dist=dist[v];u=v;}}if(u==-1)break;// 剩余顶点不可达visited[u]=1;// 步骤2: 松弛u的所有邻居for(intv=0;v<g->vertex_count;v++){if(!visited[v]&&g->matrix[u][v]!=INF){intnew_dist=dist[u]+g->matrix[u][v];if(new_dist<dist[v]){dist[v]=new_dist;}}}}}

算法复杂度:

  • 时间复杂度O ( ∣ V ∣ 2 ) O(|V|^2)O(V2),适合稠密图。
  • 空间复杂度O ( ∣ V ∣ ) O(|V|)O(V)
  • 优化方向:使用**优先队列(最小堆)**可将时间复杂度降为O ( ( ∣ V ∣ + ∣ E ∣ ) log ⁡ ∣ V ∣ ) O((|V|+|E|) \log |V|)O((V+E)logV),适合稀疏图。

第四部分:Bellman-Ford算法

1. 核心思想

动态规划/松弛。通过对所有边进行∣ V ∣ − 1 |V|-1V1轮松弛操作,逐步逼近最短路径。可以处理负权边,并能检测出图中是否存在从源点可达的负权环

原理:在无负权环的图中,任意两点间的最短路径最多包含∣ V ∣ − 1 |V|-1V1条边。因此通过∣ V ∣ − 1 |V|-1V1轮全局松弛足以找到所有最短路径。


2. 算法步骤

  1. 初始化:dist[s] = 0dist[v] = INFv ≠ s)。
  2. 进行∣ V ∣ − 1 |V|-1V1轮迭代,每轮:
    • 遍历图中的所有边( u , v ) (u, v)(u,v)
    • 对每条边执行松弛操作:如果dist[u] + w(u, v) < dist[v],则更新dist[v] = dist[u] + w(u, v)
  3. 负权环检测:再进行一轮所有边的遍历。如果仍有边能被松弛,则说明图中存在从源点可达的负权环,最短路径无意义。

3. C语言实现

// Bellman-Ford算法 (使用边列表存储图更合适,此处为演示使用邻接矩阵遍历所有边)voidbellman_ford(GraphMatrixWeighted*g,intsrc,intdist[]){// 初始化for(inti=0;i<g->vertex_count;i++){dist[i]=INF;}dist[src]=0;// 步骤1: 进行 |V|-1 轮松弛for(inti=0;i<g->vertex_count-1;i++){// 遍历所有边 (u, v)for(intu=0;u<g->vertex_count;u++){for(intv=0;v<g->vertex_count;v++){if(g->matrix[u][v]!=INF){// 存在边intnew_dist=dist[u]+g->matrix[u][v];if(new_dist<dist[v]){dist[v]=new_dist;}}}}}// 步骤2: 检测负权环 (可选,如果需要检测)for(intu=0;u<g->vertex_count;u++){for(intv=0;v<g->vertex_count;v++){if(g->matrix[u][v]!=INF&&dist[u]+g->matrix[u][v]<dist[v]){printf("图中存在从源点可达的负权环!\n");return;}}}}

算法复杂度:

  • 时间复杂度O ( ∣ V ∣ ⋅ ∣ E ∣ ) O(|V| \cdot |E|)O(VE),使用邻接矩阵为O ( ∣ V ∣ 3 ) O(|V|^3)O(V3),使用边列表为O ( ∣ V ∣ ⋅ ∣ E ∣ ) O(|V| \cdot |E|)O(VE)
  • 空间复杂度O ( ∣ V ∣ ) O(|V|)O(V)

第五部分:总结与对比

算法对比表

特性Dijkstra算法Bellman-Ford算法
适用图类型非负权图任意图(可含负权边)
核心思想贪心动态规划/松弛
时间复杂度O ( ∣ V ∣ 2 ) O(|V|^2)O(V2)O ( ( ∣ V ∣ + ∣ E ∣ ) log ⁡ ∣ V ∣ ) O((|V|+|E|)\log|V|)O((V+E)logV)O ( ∣ V ∣ ⋅ ∣ E ∣ ) O(|V| \cdot |E|)O(VE)
空间复杂度O ( ∣ V ∣ ) O(|V|)O(V)O ( ∣ V ∣ ) O(|V|)O(V)
功能求单源最短路径求单源最短路径,可检测负权环
优点在非负权图中效率高功能强大,适用范围广
缺点不能处理负权边时间复杂度高

选择指南

  1. 绝大多数情况(边权非负):优先使用Dijkstra算法(尤其是堆优化版本)。例如:道路导航、网络路由。
  2. 图含有负权边:必须使用Bellman-Ford算法。例如:金融套利模型、某些特殊约束问题。
  3. 需要检测负权环:使用 Bellman-Ford 算法。



觉得文章有帮助?别忘了:

👍 点赞 👍- 给我一点鼓励
⭐ 收藏 ⭐- 方便以后查看
🔔 关注 🔔- 获取更新通知



标签:#C语言#图论#最短路径#Dijkstra#Bellman-Ford#算法

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/24 11:18:43

LangChain 1.0智能体核心组件全解析:从架构到实战

在人工智能飞速发展的今天&#xff0c;单纯的语言模型已经无法满足复杂任务的需求。就像一个聪明的大脑如果没有手脚&#xff0c;也难以完成实际工作。LangChain 1.0的智能体&#xff08;Agent&#xff09;正是为了解决这一问题&#xff0c;将语言模型与工具、中间件、记忆等组…

作者头像 李华
网站建设 2025/12/24 17:52:55

快速排序的理解与实践(c语言实现)

快速排序的理解与实践 排序是计算机程序中常见的操作&#xff0c;而快速排序以其高效性成为许多程序员的优先选择。第一次接触快速排序时&#xff0c;我被它巧妙的分治思想所吸引——将一个大问题分解为若干小问题&#xff0c;逐个解决后再合并结果。这种思维方式不仅适用于排序…

作者头像 李华
网站建设 2025/12/24 21:16:39

Product Hunt 每日热榜 | 2025-12-14

1. PlanEat AI 标语&#xff1a;人工智能将你的健康目标变成一个为期7天的菜单和购物清单。 介绍&#xff1a;大多数应用程序给你提供一堆食谱&#xff0c;而聊天机器人则让你淹没在文字中。PlanEat AI 将你的健康数据和饮食规则整理成一个可行的每周计划和分类购物清单&…

作者头像 李华
网站建设 2025/12/24 12:50:35

实验实验实验

这玩意儿直接html吗&#xff0c;前端和后端直接连接&#xff0c;直接打包。我可以理解为这是专属小程序的debug&#xff0c;必须要有源代码。

作者头像 李华