[算法] 最短路径条数问题

本文介绍了一种基于广度优先搜索(BFS)的算法,用于寻找从源点到终点的所有最短路径数目。该算法适用于所有边权值相同的无向连通图,通过维护两个数组分别记录每个节点的最短路径长度及路径数量,最终得出目标节点的最短路径总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

给定如图所示的无向连通图,假定图中所有边的权值均为1,显然,从源点A到终点T的最短路径有多条,求不同的最短路径数目。
这里写图片描述

分析

权值相同的最短路径问题,则单源点DijkstraDijkstra算法退化为BFS广度优先算法, 使用DijkstraDijkstra算法只能求的到各个节点的最短路径,但不能求得最短路径的条数。假定起点为0,终点为N,数组step[0...N1]step[0...N−1]step[i]step[i]代表到第ii个节点的最短路径长度,数组pathNum[0...N1]pathNum[i]pathNum[i]代表到第ii个节点的最短路径条数,首先:

  • step[0...N1]初始化为0
    • pathNum[0...N1]pathNum[0...N−1]初始化为0
    • pathNum[0]pathNum[0]为1,即起点到起点的路径数量为1

    从当前节点ii扩展到邻接节点j时:

    • step[j]step[j]为0,则表示从未到达过节点jj,则:

      • step[j]=step[i]+1pathNum[j]=pathNum[i]pathNum[j]=pathNum[i]

      • step[j]==step[i]+1step[j]==step[i]+1,表示已经有从其他节点到达过节点jj,并且路径长度和从节点i到节点jj的路径长度相等,则已有到达j的路径数加上经过ii到达j的路径数为当前从源点到达节点jj的路径数量:

        • pathNum[j]+=pathNum[i]

        • step[j]>step[i]+1step[j]>step[i]+1,则已有的经过其他节点到达节点jj的路径长度要比经过节点i到达节点jj路径长度要长,则需要更新到达节点j的路径长度和路径数量,即:

          • step[j]=step[i]+1step[j]=step[i]+1pathNum[j]=pathNum[i]pathNum[j]=pathNum[i]
        • 当扩展到节点NN时,终止算法

        代码实现

        借助广度优先搜索的思想遍历所有节点,具体实现如CalPathNum.hpp所示:

        #ifndef CalPathNum_hpp
        #define CalPathNum_hpp
        
        #include <stdio.h>
        #include <string.h>
        #include <queue>
        
        const int nodeCnt = 16;
        int calculatePathNum(int graph[nodeCnt][nodeCnt]) {
            int step[nodeCnt]; // step[i]表示到第i个节点的步数
            int pathNum[nodeCnt]; // pathNum[i]表示到第i个节点的最短路径条数
            memset(step, 0, sizeof(int)*nodeCnt);
            memset(pathNum, 0, sizeof(int)*nodeCnt);
            pathNum[0] = 1; // 起点到起点的路径数量为1
            std::queue<int> q; // 广度优先搜索中保存当前到达的节点
            q.push(0); // 首先到达起点
            int from, i, s;
            while (!q.empty()) {
                from = q.front();
                q.pop();
                s = step[from] + 1; // 和from节点相邻的节点的路径长度
                for (i = 1; i < nodeCnt; i++) { // 0是起点,不需要遍历
                    if (graph[from][i] == 1) { // 从from到i是连通的
                        if (step[i] == 0 || step[i] > s) { // i节点从未到达过或发现更快的路径
                            step[i] = s;
                            pathNum[i] = pathNum[from];
                            q.push(i); // 将和from相邻的节点入队,相当于广度优先搜索中form节点的下一层节点入队
                        } else if(s == step[i]){ // 发现相同长度的路径
                            pathNum[i] += pathNum[from];
                        }
                    }
                }
            }
            return pathNum[nodeCnt-1];
        }
        
        #endif /* CalPathNum_hpp */

        测试代码如main.cpp:

        #include "CalPathNum.hpp"
        
        int main(int argc, const char * argv[]) {
            // 测试最短路径条数
            int graph[16][16];
            memset(graph, 0, sizeof(int) * 16 * 16);
            graph[0][1] = graph[0][4] = 1;
            graph[1][5] = graph[1][0] = graph[1][2] = 1;
            graph[2][1] = graph[2][6] = graph[2][3] = 1;
            graph[3][2] = graph[3][7] = 1;
            graph[4][0] = graph[4][5] = 1;
            graph[5][1] = graph[5][4] = graph[5][6] = graph[5][9] = 1;
            graph[6][2] = graph[6][5] = graph[6][7] = graph[6][10] = 1;
            graph[7][3] = graph[7][6] = 1;
            graph[8][9] = graph[8][12] = 1;
            graph[9][8] = graph[9][13] = graph[9][10] = 1;
            graph[10][9] = graph[10][14] = graph[10][11] = 1;
            graph[11][10] = graph[11][15] = 1;
            graph[12][8] = graph[12][13] = 1;
            graph[13][9] = graph[13][12] = graph[13][14] = 1;
            graph[14][10] = graph[14][13] = graph[14][15] = 1;
            graph[15][11] = graph[15][14] = 1;
            printf("%d\n", calculatePathNum(graph));
        
            return 0;
        }

        该算法事实上是广度优先搜索的一个应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值