网站建设需要哪些职位商城网站建设案例
2026/1/21 15:31:45 网站建设 项目流程
网站建设需要哪些职位,商城网站建设案例,app开发公司哪,ui设计效果图在图论的世界里#xff0c;“最短路径” 是个高频需求 —— 比如从家到公司的最优路线、网络中数据传输的最短延迟。我们知道 Dijkstra 算法很经典#xff0c;但它怕负权边#xff1b;Bellman-Ford 算法能处理负权边#xff0c;却慢得让人着急。今天要讲的 SPFA 算法#…在图论的世界里“最短路径” 是个高频需求 —— 比如从家到公司的最优路线、网络中数据传输的最短延迟。我们知道 Dijkstra 算法很经典但它怕负权边Bellman-Ford 算法能处理负权边却慢得让人着急。今天要讲的 SPFA 算法就是两者的 “完美折中”既能搞定负权边效率又高还能顺便检测讨厌的负权回路。一、先搞懂我们要解决什么问题假设有这样一张图5 个顶点 0-4边的权重有正有负0→1权重 2、0→2权重 41→2权重 - 1负权边、1→3权重 72→4权重 3、3→4权重 1我们的目标是从顶点 0 出发找到它到其他所有顶点的最短距离同时判断图里有没有 “负权回路”就是绕一圈下来总权重为负的环比如 A→B→A 权重和为 - 2绕得越多距离越短根本没有最优解。这时候 Dijkstra 算法会 “罢工”负权边会让它算错Bellman-Ford 又太慢SPFA 就该登场了。二、SPFA 的核心思路只跟 “有用的人” 打交道SPFA 的全称是 Shortest Path Faster Algorithm翻译过来就是 “更快的最短路径算法”。它快在哪里核心秘诀就是不做无用功只处理能帮我们缩短路径的顶点。我们可以用一个生活例子理解假设你要通知朋友 “最新的最短路径消息”Bellman-Ford 的做法是不管朋友有没有收到过每天挨家挨户敲门通知遍历所有边连续通知 n-1 天n 是顶点数效率极低。而 SPFA 的做法是先告诉起点比如顶点 0“你到自己的距离是 0”把它拉进一个 “消息队列” 里。从队列里拿出顶点 0告诉它的邻居1 和 2“从 0 到你们的距离是 2 和 4赶紧记下来”然后把 1 和 2 也拉进队列因为它们现在有了新消息可能能帮到它们的邻居。再从队列里拿出顶点 1告诉它的邻居2 和 3“从 0→1→2 的距离是 2(-1)1比之前的 4 更近快更新从 0→1→3 的距离是 9”。顶点 2 的距离被更新了就把它重新拉进队列顶点 3 是第一次收到消息也拉进队列。继续这个过程直到队列空了 —— 此时所有人都拿到了最短距离。这个 “消息队列” 就是 SPFA 的灵魂它只缓存 “有新消息要传递” 的顶点不用遍历所有边效率自然大幅提升。三、代码拆解一步步看懂 SPFA 怎么工作下面结合开头的代码用 “人话” 拆解每个关键部分不用怕代码我们只看核心逻辑。1. 先搭好 “图” 的架子图由 “顶点” 和 “边” 组成我们用 “邻接表” 来存储就像每个顶点都有一个 “通讯录”记录它能直达的邻居和距离// 定义一条边包含终点和权重struct Edge {int to; // 邻居终点int weight; // 距离权重可正可负Edge(int t, int w) : to(t), weight(w) {} // 初始化边};// 定义图每个顶点的通讯录集合邻接表typedef vectorvectorEdge Graph;比如顶点 0 的通讯录就是[Edge(1,2), Edge(2,4)]表示能直达 1距离 2和 2距离 4。2. 核心工具三个数组 一个队列SPFA 需要四个关键 “工具”缺一不可dist[]距离数组记录起点到每个顶点的最短距离初始都是 “无穷大”表示暂时不可达。in_queue[]标记数组记录顶点是否在队列里避免重复入队比如顶点 2 已经在队列里就不用再拉进来减少冗余。in_count[]计数数组记录顶点进队列的次数用来检测负权回路。queue消息队列缓存需要传递 “最短路径消息” 的顶点。3. 算法步骤从队列开始层层传递消息cpp运行vectorint spfa(const Graph graph, int start, bool has_negative_cycle) { int n graph.size(); // 顶点总数 vectorint dist(n, INT_MAX); // 初始距离都是无穷大 vectorbool in_queue(n, false); // 标记是否在队列中 vectorint in_count(n, 0); // 记录入队次数 queueint q; // 消息队列 // 初始化起点自己到自己的距离是0加入队列 dist[start] 0; q.push(start); in_queue[start] true; in_count[start] 1; has_negative_cycle false; // 队列不空就继续传递消息 while (!q.empty()) { int u q.front(); // 取出队列里的“消息传递者”u q.pop(); in_queue[u] false; // 标记u已经出队后续可再入队 // 遍历u的所有邻居查看u的通讯录 for (auto e : graph[u]) { int v e.to; // 邻居v int w e.weight; // u到v的距离 // 松弛操作如果u→v的路径更短就更新v的距离 if (dist[u] ! INT_MAX dist[v] dist[u] w) { dist[v] dist[u] w; // 更新最短距离 // 如果v不在队列里就拉进队列让它也传递消息 if (!in_queue[v]) { q.push(v); in_queue[v] true; in_count[v]; // 入队次数1 // 检测负权回路入队次数n说明有问题 if (in_count[v] n) { has_negative_cycle true; return dist; // 直接返回不用再算了 } } } } } return dist; }这里有两个关键知识点必须讲透1什么是 “松弛操作”“松弛” 就是 “放松限制找到更优解”。比如之前我们知道 0→2 的距离是 4但后来发现 0→1→2 的距离是 2(-1)1比 4 更短 —— 这时候就 “松弛” 了 0→2 的距离限制把最短距离更新为 1。这是所有最短路径算法的核心就像你本来以为走大路最快后来发现有条小路更近就赶紧调整路线。2怎么检测负权回路正常情况下一个顶点的最短路径最多经过 n-1 个顶点比如 5 个顶点最短路径最多 4 条边不会绕圈。所以一个顶点最多需要进队列 n-1 次每次更新一条更短的路径。如果某个顶点进队列的次数 n比如 5 个顶点顶点 2 进了 5 次队列说明它在绕圈 —— 而且是绕一次距离就变短的负权回路比如 2→3→2权重和为 - 2这时候就可以确定存在负权回路直接返回结果就行。4. 测试一下看看结果对不对我们用开头的图测试起点是 0cpp运行int main() { int vertex_num 5; Graph graph(vertex_num); // 添加边顶点→邻居距离 graph[0].push_back(Edge(1, 2)); graph[0].push_back(Edge(2, 4)); graph[1].push_back(Edge(2, -1)); graph[1].push_back(Edge(3, 7)); graph[2].push_back(Edge(4, 3)); graph[3].push_back(Edge(4, 1)); int start_vertex 0; bool has_neg_cycle false; vectorint shortest_dist spfa(graph, start_vertex, has_neg_cycle); // 打印结果 printDistance(shortest_dist, start_vertex); cout 是否存在可到达的负权回路 (has_neg_cycle ? 是 : 否) endl; return 0; }运行结果如下plaintext从 0 出发的最短距离为 到顶点 0 为0 到顶点 1 为2 到顶点 2 为1 到顶点 3 为9 到顶点 4 为4 是否存在可到达的负权回路否完全符合我们的预期0→1→2→4 的距离是 2(-1)34是到 4 的最短路径图里没有负权回路所以输出 “否”。四、关键问题为什么一定要用队列很多人会问不用队列行不行答案是行但会退化成效率极低的 Bellman-Ford 算法。我们再对比一下Bellman-Ford不管顶点有没有更新每次都遍历所有边比如 1000 条边就要遍历 1000 次时间复杂度 O (n*m)慢得离谱。SPFA用队列只处理 “距离被更新的顶点” 的邻居相当于 “精准打击”不用做无用功。实际场景中时间复杂度接近 O (m)m 是边数比 Bellman-Ford 快几个量级。队列就像一个 “高效的消息分发中心”只让有新消息的人去传递而不是让所有人都乱传一通 —— 这就是 SPFA 快的根本原因。五、SPFA 的适用场景有负权边但没有负权回路或需要检测负权回路的图稀疏图边数少的图SPFA 的效率优势更明显单源最短路径从一个起点到所有顶点的最短距离。如果图里全是正权边Dijkstra 算法加优先队列可能更快但如果有负权边SPFA 就是更稳妥的选择。总结SPFA 算法其实一点都不复杂核心就是三句话用队列缓存 “有新消息距离更新” 的顶点只处理这些顶点的邻居避免无用功用 “松弛操作” 不断更新最短距离用 “入队次数” 检测负权回路避免无限循环。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询