跳转至

最短路径算法

概述

最短路径问题是图论中的经典问题,目标是找到图中两个顶点之间的最短路径。根据问题类型,可分为:

问题类型
  • 单源最短路径:求一个源点到所有其他点的最短路径
  • 多源最短路径:求任意两点之间的最短路径
  • 单对最短路径:求特定两点之间的最短路径

生活类比

想象你使用导航软件规划路线:从当前位置到目的地可能有多条路径,导航会找出最短(或最快)的那条。最短路径算法就是解决这类问题的数学工具。

问题分类与算法选择

graph TB
    A["最短路径问题"] --> B{"边的权重"}
    
    B -->|非负权重| C["Dijkstra算法"]
    B -->|存在负权重| D{"有负权回路?"}
    
    D -->|无负权回路| E["Bellman-Ford<br>SPFA"]
    D -->|有负权回路| F["最短路径不存在<br>(可无限缩短)"]
    
    A --> G["多源最短路径"]
    G --> H["Floyd-Warshall<br>Johnson"]
    
    style C fill:#E8F5E9,stroke:#4CAF50
    style F fill:#FFF3E0,stroke:#FF9800

Dijkstra 算法

算法原理

Dijkstra 算法由荷兰计算机科学家 Edsger W. Dijkstra 于 1956 年提出,适用于**非负权重图**的单源最短路径问题。

flowchart TB
    subgraph "Dijkstra 算法核心思想"
        A["初始化: dist[source]=0<br>其他点距离=∞"] --> B["选择未访问中<br>距离最小的点"]
        B --> C["标记该点已访问"]
        C --> D["松弛其所有邻居"]
        D --> E{"所有点都访问了?"}
        E -->|否| B
        E -->|是| F["算法结束"]
    end
    
    style A fill:#E3F2FD,stroke:#2196F3
    style F fill:#E8F5E9,stroke:#4CAF50

松弛操作

松弛(Relaxation)操作

对边 (u, v),如果 dist[u] + weight(u,v) < dist[v],则更新 dist[v]:

dist[v] = min(dist[v], dist[u] + weight(u, v))

含义:如果经过 u 到达 v 的路径更短,则更新 v 的最短距离。

算法执行过程

Text Only
示例图:
        10
    A ──────→ B
    │╲        │╲
   5│ ╲15    1│ ╲2
    ↓  ╲      ↓  ╲
    C───→E───→D───→F
       2     3     4

从 A 出发,求到所有点的最短路径

步骤1: 初始化
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │  ∞  │  ∞  │  ∞  │  ∞  │  ∞  │      │
│ 状态   │ ✓   │     │     │     │     │     │      │
└────────────────────────────────────────────────────┘
           当前点: A

步骤2: 选择 A,松弛邻居 B, C, E
┌────────────────────────────────────────────────────┐
│ 松弛 B: dist[A] + 10 = 10 < ∞ → dist[B] = 10      │
│ 松弛 C: dist[A] + 5 = 5 < ∞ → dist[C] = 5         │
│ 松弛 E: dist[A] + 15 = 15 < ∞ → dist[E] = 15      │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │ 10  │  5  │  ∞  │ 15  │  ∞  │      │
│ 状态   │ ✓   │     │ ✓   │     │     │     │      │
└────────────────────────────────────────────────────┘
           当前点: C (距离最小)

步骤3: 选择 C,松弛邻居 E
┌────────────────────────────────────────────────────┐
│ 松弛 E: dist[C] + 2 = 7 < 15 → dist[E] = 7 ✓      │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │ 10  │  5  │  ∞  │  7  │  ∞  │      │
│ 状态   │ ✓   │     │ ✓   │     │ ✓   │     │      │
└────────────────────────────────────────────────────┘
           当前点: E (距离最小)

步骤4: 选择 E,松弛邻居 D
┌────────────────────────────────────────────────────┐
│ 松弛 D: dist[E] + 3 = 10 < ∞ → dist[D] = 10       │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │ 10  │  5  │ 10  │  7  │  ∞  │      │
│ 状态   │ ✓   │     │ ✓   │     │ ✓   │     │      │
└────────────────────────────────────────────────────┘
           当前点: B 或 D (都是10)

步骤5: 选择 B,松弛邻居 D, F
┌────────────────────────────────────────────────────┐
│ 松弛 D: dist[B] + 1 = 11 > 10 → 不更新            │
│ 松弛 F: dist[B] + 2 = 12 < ∞ → dist[F] = 12       │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │ 10  │  5  │ 10  │  7  │ 12  │      │
│ 状态   │ ✓   │ ✓   │ ✓   │     │ ✓   │     │      │
└────────────────────────────────────────────────────┘
           当前点: D

步骤6: 选择 D,松弛邻居 F
┌────────────────────────────────────────────────────┐
│ 松弛 F: dist[D] + 4 = 14 > 12 → 不更新            │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 顶点   │  A  │  B  │  C  │  D  │  E  │  F  │      │
├────────────────────────────────────────────────────┤
│ 距离   │  0  │ 10  │  5  │ 10  │  7  │ 12  │      │
│ 状态   │ ✓   │ ✓   │ ✓   │ ✓   │ ✓   │ ✓   │      │
└────────────────────────────────────────────────────┘

最终结果:
A→A: 0
A→B: 10 (A→B)
A→C: 5 (A→C)
A→D: 10 (A→C→E→D 或 A→B→D)
A→E: 7 (A→C→E)
A→F: 12 (A→B→F)

基本实现(邻接矩阵)

C
#include <stdio.h>
#include <limits.h>

#define V 6
#define INF INT_MAX

// 找到未访问顶点中距离最小的
int minDistance(int dist[], int visited[]) {
    int min = INF, minIndex = -1;
    
    for (int v = 0; v < V; v++) {
        if (!visited[v] && dist[v] <= min) {
            min = dist[v];
            minIndex = v;
        }
    }
    
    return minIndex;
}

// 打印最短路径
void printPath(int parent[], int j) {
    if (parent[j] == -1) {
        printf("%c", 'A' + j);
        return;
    }
    printPath(parent, parent[j]);
    printf(" -> %c", 'A' + j);
}

// Dijkstra 算法
void dijkstra(int graph[V][V], int src) {
    int dist[V];       // 最短距离数组
    int visited[V];    // 访问标记
    int parent[V];     // 路径前驱
    
    // 初始化
    for (int i = 0; i < V; i++) {
        dist[i] = INF;
        visited[i] = 0;
        parent[i] = -1;
    }
    dist[src] = 0;
    
    printf("Dijkstra 算法执行过程:\n\n");
    
    // 对所有顶点执行
    for (int count = 0; count < V - 1; count++) {
        // 选择未访问中距离最小的顶点
        int u = minDistance(dist, visited);
        if (u == -1) break;  // 无法到达的顶点
        
        visited[u] = 1;
        printf("选择顶点 %c, 距离=%d\n", 'A' + u, dist[u]);
        
        // 松弛所有邻居
        for (int v = 0; v < V; v++) {
            if (!visited[v] && graph[u][v] != INF && 
                dist[u] != INF && 
                dist[u] + graph[u][v] < dist[v]) {
                
                dist[v] = dist[u] + graph[u][v];
                parent[v] = u;
                printf("  松弛 %c: dist[%c] = %d\n", 
                       'A' + v, 'A' + v, dist[v]);
            }
        }
        printf("\n");
    }
    
    // 打印结果
    printf("\n最短路径结果:\n");
    printf("目标\t距离\t路径\n");
    for (int i = 0; i < V; i++) {
        printf("%c\t", 'A' + i);
        if (dist[i] == INF) {
            printf("不可达\t-\n");
        } else {
            printf("%d\t", dist[i]);
            printPath(parent, i);
            printf("\n");
        }
    }
}

int main() {
    // 邻接矩阵表示的图
    int graph[V][V] = {
        //A    B    C    D    E    F
        {INF,  10,   5, INF,  15, INF},  // A
        {INF, INF, INF,   1, INF,   2},  // B
        {INF, INF, INF, INF,   2, INF},  // C
        {INF, INF, INF, INF, INF,   4},  // D
        {INF, INF, INF,   3, INF, INF},  // E
        {INF, INF, INF, INF, INF, INF}   // F
    };
    
    dijkstra(graph, 0);  // 从 A 开始
    
    return 0;
}

优先队列优化版本

graph LR
    subgraph "时间复杂度对比"
        A["朴素实现<br>O(V²)"] --> B["使用优先队列<br>O(E + V)logV"]
    end
    
    style B fill:#E8F5E9,stroke:#4CAF50
C
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define V 6
#define INF INT_MAX

typedef struct {
    int vertex;
    int distance;
} HeapNode;

typedef struct {
    HeapNode *nodes;
    int size;
    int capacity;
} MinHeap;

// 创建最小堆
MinHeap* createMinHeap(int capacity) {
    MinHeap *heap = (MinHeap*)malloc(sizeof(MinHeap));
    heap->nodes = (HeapNode*)malloc(capacity * sizeof(HeapNode));
    heap->size = 0;
    heap->capacity = capacity;
    return heap;
}

// 交换堆节点
void swapHeapNodes(HeapNode *a, HeapNode *b) {
    HeapNode temp = *a;
    *a = *b;
    *b = temp;
}

// 最小堆化
void minHeapify(MinHeap *heap, int idx) {
    int smallest = idx;
    int left = 2 * idx + 1;
    int right = 2 * idx + 2;
    
    if (left < heap->size && 
        heap->nodes[left].distance < heap->nodes[smallest].distance)
        smallest = left;
    
    if (right < heap->size && 
        heap->nodes[right].distance < heap->nodes[smallest].distance)
        smallest = right;
    
    if (smallest != idx) {
        swapHeapNodes(&heap->nodes[idx], &heap->nodes[smallest]);
        minHeapify(heap, smallest);
    }
}

// 插入堆
void insertHeap(MinHeap *heap, int vertex, int distance) {
    if (heap->size == heap->capacity) return;
    
    int i = heap->size++;
    heap->nodes[i].vertex = vertex;
    heap->nodes[i].distance = distance;
    
    // 向上调整
    while (i != 0 && 
           heap->nodes[(i-1)/2].distance > heap->nodes[i].distance) {
        swapHeapNodes(&heap->nodes[i], &heap->nodes[(i-1)/2]);
        i = (i - 1) / 2;
    }
}

// 提取最小值
HeapNode extractMin(MinHeap *heap) {
    if (heap->size == 0) {
        HeapNode empty = {-1, INF};
        return empty;
    }
    
    HeapNode min = heap->nodes[0];
    heap->nodes[0] = heap->nodes[--heap->size];
    minHeapify(heap, 0);
    
    return min;
}

// Dijkstra 优先队列优化版本
void dijkstraOptimized(int graph[V][V], int src) {
    int dist[V];
    int parent[V];
    int visited[V] = {0};
    
    for (int i = 0; i < V; i++) {
        dist[i] = INF;
        parent[i] = -1;
    }
    dist[src] = 0;
    
    MinHeap *heap = createMinHeap(V * V);
    insertHeap(heap, src, 0);
    
    printf("优先队列优化的 Dijkstra:\n\n");
    
    while (heap->size > 0) {
        HeapNode min = extractMin(heap);
        int u = min.vertex;
        
        if (visited[u]) continue;
        visited[u] = 1;
        
        printf("处理顶点 %c, 距离=%d\n", 'A' + u, dist[u]);
        
        // 遍历所有邻接顶点
        for (int v = 0; v < V; v++) {
            if (graph[u][v] != INF && !visited[v]) {
                int newDist = dist[u] + graph[u][v];
                if (newDist < dist[v]) {
                    dist[v] = newDist;
                    parent[v] = u;
                    insertHeap(heap, v, newDist);
                    printf("  更新 %c: dist=%d\n", 'A' + v, dist[v]);
                }
            }
        }
    }
    
    printf("\n最短距离:\n");
    for (int i = 0; i < V; i++) {
        printf("%c: %d\n", 'A' + i, dist[i] == INF ? -1 : dist[i]);
    }
    
    free(heap->nodes);
    free(heap);
}

Bellman-Ford 算法

算法原理

Bellman-Ford 算法适用于**包含负权边**的图,并能检测负权回路。

flowchart TB
    subgraph "Bellman-Ford 算法流程"
        A["初始化: dist[source]=0<br>其他=∞"] --> B["对所有边进行V-1轮松弛"]
        B --> C["再对所有边检查一次"]
        C --> D{"还能松弛?"}
        D -->|是| E["存在负权回路"]
        D -->|否| F["算法结束<br>得到最短路径"]
    end
    
    style E fill:#FFF3E0,stroke:#FF9800
    style F fill:#E8F5E9,stroke:#4CAF50

为什么需要 V-1 轮松弛?

原理说明

最短路径最多包含 V-1 条边(经过 V 个顶点)

第 i 轮松弛后,确定了最多经过 i 条边的最短路径

因此 V-1 轮后,所有最短路径都已确定

Text Only
示例图(含负权边):

    A ──4──→ B
    │        │
    3        -2
    ↓        ↓
    C ──1──→ D

从 A 出发:

初始化:
┌─────────────────────────────────┐
│ 顶点  │  A  │  B  │  C  │  D  │
├─────────────────────────────────┤
│ 距离  │  0  │  ∞  │  ∞  │  ∞  │
└─────────────────────────────────┘

第1轮松弛(对所有边):
┌─────────────────────────────────────────────────────┐
│ 边(A,B): dist[A]+4=4 < ∞ → dist[B]=4               │
│ 边(A,C): dist[A]+3=3 < ∞ → dist[C]=3               │
│ 边(B,D): dist[B]+(-2)=2 < ∞ → dist[D]=2            │
│ 边(C,D): dist[C]+1=4 > 2 → 不更新                  │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────┐
│ 顶点  │  A  │  B  │  C  │  D  │
├─────────────────────────────────┤
│ 距离  │  0  │  4  │  3  │  2  │
└─────────────────────────────────┘

第2轮松弛:
┌─────────────────────────────────────────────────────┐
│ 边(A,B): 0+4=4 ≥ 4 → 不更新                        │
│ 边(A,C): 0+3=3 ≥ 3 → 不更新                        │
│ 边(B,D): 4+(-2)=2 ≥ 2 → 不更新                     │
│ 边(C,D): 3+1=4 > 2 → 不更新                        │
└─────────────────────────────────────────────────────┘
没有变化,算法收敛

最终结果: A→D 的最短距离是 2 (A→B→D)

负权回路检测

Text Only
负权回路示例:

    A ──3──→ B
    ↑        │
    │        -4
    └─────────┘

从 A 出发:

每轮松弛后 dist[B] 和 dist[A] 都会减小:
┌─────────────────────────────────────────────────────┐
│ 第1轮: dist[A]=0, dist[B]=3                        │
│ 第2轮: dist[A]=-1, dist[B]=2  (A→B→A 形成负回路)  │
│ 第3轮: dist[A]=-5, dist[B]=-2                      │
│ ...无限减小...                                      │
└─────────────────────────────────────────────────────┘

结论: 存在负权回路,最短路径无定义

实现

C
#include <stdio.h>
#include <limits.h>

#define V 4
#define E 4
#define INF INT_MAX

typedef struct {
    int src, dest, weight;
} Edge;

void bellmanFord(Edge edges[], int src) {
    int dist[V];
    int parent[V];
    
    // 初始化
    for (int i = 0; i < V; i++) {
        dist[i] = INF;
        parent[i] = -1;
    }
    dist[src] = 0;
    
    printf("Bellman-Ford 算法执行:\n\n");
    
    // V-1 轮松弛
    for (int i = 1; i < V; i++) {
        printf("第 %d 轮松弛:\n", i);
        int updated = 0;
        
        for (int j = 0; j < E; j++) {
            int u = edges[j].src;
            int v = edges[j].dest;
            int w = edges[j].weight;
            
            if (dist[u] != INF && dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                parent[v] = u;
                updated = 1;
                printf("  边(%c,%c): dist[%c] = %d\n", 
                       'A' + u, 'A' + v, 'A' + v, dist[v]);
            }
        }
        
        if (!updated) {
            printf("  无更新,提前终止\n");
            break;
        }
    }
    
    // 检查负权回路
    printf("\n检查负权回路:\n");
    int hasNegativeCycle = 0;
    for (int j = 0; j < E; j++) {
        int u = edges[j].src;
        int v = edges[j].dest;
        int w = edges[j].weight;
        
        if (dist[u] != INF && dist[u] + w < dist[v]) {
            printf("检测到负权回路!边(%c,%c) 还能松弛\n", 
                   'A' + u, 'A' + v);
            hasNegativeCycle = 1;
        }
    }
    
    if (!hasNegativeCycle) {
        printf("无负权回路\n\n");
        printf("最短距离:\n");
        for (int i = 0; i < V; i++) {
            printf("%c: ", 'A' + i);
            if (dist[i] == INF) printf("不可达\n");
            else printf("%d\n", dist[i]);
        }
    }
}

int main() {
    Edge edges[E] = {
        {0, 1, 4},  // A → B, weight 4
        {0, 2, 3},  // A → C, weight 3
        {1, 3, -2}, // B → D, weight -2
        {2, 3, 1}   // C → D, weight 1
    };
    
    bellmanFord(edges, 0);
    
    return 0;
}

SPFA 算法

SPFA(Shortest Path Faster Algorithm)是 Bellman-Ford 的队列优化版本。

flowchart TB
    subgraph "SPFA vs Bellman-Ford"
        A["Bellman-Ford<br>每轮松弛所有边"] --> B["O(VE)"]
        C["SPFA<br>只松弛可能改变的点"] --> D["O(kE) 平均"]
    end
    
    style D fill:#E8F5E9,stroke:#4CAF50
C
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define V 4
#define INF INT_MAX

typedef struct {
    int vertex;
    int weight;
} Edge;

typedef struct {
    Edge edges[100];
    int count;
} AdjList;

void spfa(AdjList adj[], int src) {
    int dist[V];
    int inQueue[V] = {0};
    int count[V] = {0};  // 入队次数,检测负权回路
    
    for (int i = 0; i < V; i++) {
        dist[i] = INF;
    }
    dist[src] = 0;
    
    // 队列
    int queue[V * V];
    int front = 0, rear = 0;
    queue[rear++] = src;
    inQueue[src] = 1;
    
    printf("SPFA 执行过程:\n\n");
    
    while (front != rear) {
        int u = queue[front++];
        inQueue[u] = 0;
        
        printf("处理顶点 %c, dist=%d\n", 'A' + u, dist[u]);
        
        // 松弛所有邻居
        for (int i = 0; i < adj[u].count; i++) {
            int v = adj[u].edges[i].vertex;
            int w = adj[u].edges[i].weight;
            
            if (dist[u] != INF && dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                printf("  更新 %c: dist=%d\n", 'A' + v, dist[v]);
                
                if (!inQueue[v]) {
                    queue[rear++] = v;
                    inQueue[v] = 1;
                    count[v]++;
                    
                    if (count[v] >= V) {
                        printf("\n检测到负权回路!\n");
                        return;
                    }
                }
            }
        }
    }
    
    printf("\n最短距离:\n");
    for (int i = 0; i < V; i++) {
        printf("%c: %d\n", 'A' + i, dist[i] == INF ? -1 : dist[i]);
    }
}

Floyd-Warshall 算法

算法原理

Floyd-Warshall 是**多源最短路径**算法,使用动态规划思想。

flowchart TB
    subgraph "动态规划思想"
        A["dp[i][j][k] = 从i到j<br>只经过顶点{0,1,...,k}的最短路径"] --> B["状态转移"]
        B --> C["dp[i][j][k] = min(<br>dp[i][j][k-1],<br>dp[i][k][k-1] + dp[k][j][k-1]<br>)"]
    end
核心思想

对于中间顶点 k,从 i 到 j 的最短路径有两种情况:

  1. 不经过 k:保持原来的距离
  2. 经过 k:距离 = dist[i][k] + dist[k][j]

取两者的较小值即可。

算法执行过程

Text Only
示例图:
    0 ──3──→ 1
    │        │
    8        4
    ↓        ↓
    2 ──1──→ 3

初始距离矩阵:
┌────────────────────────────────┐
│      │  0  │  1  │  2  │  3  │
├────────────────────────────────┤
│  0   │  0  │  3  │  8  │  ∞  │
│  1   │  ∞  │  0  │  ∞  │  4  │
│  2   │  ∞  │  ∞  │  0  │  1  │
│  3   │  ∞  │  ∞  │  ∞  │  0  │
└────────────────────────────────┘

k=0(考虑经过顶点0的路径):
┌────────────────────────────────────────────────────┐
│ dist[1][2] = min(∞, dist[1][0]+dist[0][2]) = ∞    │
│ dist[2][1] = min(∞, dist[2][0]+dist[0][1]) = ∞   │
│ ...(无更新)                                      │
└────────────────────────────────────────────────────┘

k=1(考虑经过顶点1的路径):
┌────────────────────────────────────────────────────┐
│ dist[0][3] = min(∞, dist[0][1]+dist[1][3]) = 7 ✓  │
│ ...                                                │
└────────────────────────────────────────────────────┘
┌────────────────────────────────┐
│      │  0  │  1  │  2  │  3  │
├────────────────────────────────┤
│  0   │  0  │  3  │  8  │  7  │ ← 更新
│  1   │  ∞  │  0  │  ∞  │  4  │
│  2   │  ∞  │  ∞  │  0  │  1  │
│  3   │  ∞  │  ∞  │  ∞  │  0  │
└────────────────────────────────┘

k=2(考虑经过顶点2的路径):
┌────────────────────────────────────────────────────┐
│ dist[0][3] = min(7, dist[0][2]+dist[2][3]) = 7    │
│ dist[1][3] = min(4, dist[1][2]+dist[2][3]) = 4   │
└────────────────────────────────────────────────────┘

k=3(考虑经过顶点3的路径):
┌────────────────────────────────────────────────────┐
│ 无更新                                            │
└────────────────────────────────────────────────────┘

最终距离矩阵:
┌────────────────────────────────┐
│      │  0  │  1  │  2  │  3  │
├────────────────────────────────┤
│  0   │  0  │  3  │  8  │  7  │
│  1   │  ∞  │  0  │  ∞  │  4  │
│  2   │  ∞  │  ∞  │  0  │  1  │
│  3   │  ∞  │  ∞  │  ∞  │  0  │
└────────────────────────────────┘

实现

C
#include <stdio.h>
#include <limits.h>

#define V 4
#define INF INT_MAX

void floydWarshall(int dist[V][V]) {
    int next[V][V];  // 用于路径重建
    
    // 初始化 next 数组
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            if (dist[i][j] != INF && i != j) {
                next[i][j] = j;
            } else {
                next[i][j] = -1;
            }
        }
    }
    
    printf("Floyd-Warshall 算法执行:\n\n");
    
    // 三重循环
    for (int k = 0; k < V; k++) {
        printf("k = %d:\n", k);
        
        for (int i = 0; i < V; i++) {
            for (int j = 0; j < V; j++) {
                if (dist[i][k] != INF && dist[k][j] != INF &&
                    dist[i][k] + dist[k][j] < dist[i][j]) {
                    
                    dist[i][j] = dist[i][k] + dist[k][j];
                    next[i][j] = next[i][k];
                    
                    printf("  dist[%d][%d] = %d\n", i, j, dist[i][j]);
                }
            }
        }
    }
    
    // 打印最终距离矩阵
    printf("\n最终距离矩阵:\n");
    printf("     ");
    for (int i = 0; i < V; i++) printf("%4d ", i);
    printf("\n");
    
    for (int i = 0; i < V; i++) {
        printf("%4d ", i);
        for (int j = 0; j < V; j++) {
            if (dist[i][j] == INF) printf("  INF ");
            else printf("%5d ", dist[i][j]);
        }
        printf("\n");
    }
    
    // 检测负权回路
    printf("\n检测负权回路:\n");
    int hasNegativeCycle = 0;
    for (int i = 0; i < V; i++) {
        if (dist[i][i] < 0) {
            printf("顶点 %d 存在负权回路\n", i);
            hasNegativeCycle = 1;
        }
    }
    if (!hasNegativeCycle) {
        printf("无负权回路\n");
    }
}

// 打印路径
void printPath(int next[V][V], int u, int v) {
    if (next[u][v] == -1) {
        printf("无路径");
        return;
    }
    printf("%d", u);
    while (u != v) {
        u = next[u][v];
        printf(" -> %d", u);
    }
}

int main() {
    int graph[V][V] = {
        {0,   3,   8, INF},
        {INF, 0, INF,   4},
        {INF, INF, 0,   1},
        {INF, INF, INF, 0}
    };
    
    floydWarshall(graph);
    
    return 0;
}

算法对比总结

graph TB
    subgraph "算法对比"
        A["Dijkstra<br>O(V²) 或 O(E+V)logV<br>非负权重"]
        B["Bellman-Ford<br>O(VE)<br>支持负权重"]
        C["SPFA<br>O(kE) 平均<br>支持负权重"]
        D["Floyd-Warshall<br>O(V³)<br>多源最短路径"]
    end
    
    A --> A1["单源<br>非负权重<br>稠密图优先朴素版<br>稀疏图优先堆优化"]
    B --> B1["单源<br>含负权重<br>检测负权回路"]
    C --> C1["单源<br>含负权重<br>通常比BF更快<br>最坏情况退化为O(VE)"]
    D --> D1["多源<br>任意权重<br>代码简洁<br>适合小图"]
    
    style A fill:#E3F2FD,stroke:#2196F3
    style B fill:#E8F5E9,stroke:#4CAF50
    style C fill:#E8F5E9,stroke:#4CAF50
    style D fill:#FFF3E0,stroke:#FF9800

详细对比表

Text Only
┌─────────────────────────────────────────────────────────────────────────────┐
│ 算法          │ 时间复杂度       │ 空间复杂度 │ 适用场景           │ 特点   │
├─────────────────────────────────────────────────────────────────────────────┤
│ Dijkstra      │ O(V²) 或         │ O(V)       │ 非负权图,单源     │ 快     │
│               │ O((V+E)logV)     │            │                    │        │
├─────────────────────────────────────────────────────────────────────────────┤
│ Bellman-Ford  │ O(VE)            │ O(V)       │ 含负权边,单源     │ 检测负 │
│               │                  │            │                    │ 权回路 │
├─────────────────────────────────────────────────────────────────────────────┤
│ SPFA          │ O(kE) 平均       │ O(V)       │ 含负权边,单源     │ 通常更 │
│               │ O(VE) 最坏       │            │                    │ 快     │
├─────────────────────────────────────────────────────────────────────────────┤
│ Floyd-Warshall│ O(V³)            │ O(V²)      │ 多源最短路径       │ 代码简 │
│               │                  │            │                    │ 洁     │
└─────────────────────────────────────────────────────────────────────────────┘

应用场景

1. 导航系统

C
// 城市间道路网络的最短路径规划
typedef struct {
    char name[50];
    double lat, lng;
} City;

void planRoute(City cities[], int graph[][MAX], int src, int dest) {
    // 使用 Dijkstra 计算最短路径
    int dist[MAX], parent[MAX];
    dijkstra(graph, src, dist, parent);
    
    printf("从 %s 到 %s 的最短路线:\n", 
           cities[src].name, cities[dest].name);
    printf("总距离: %d km\n", dist[dest]);
    
    // 打印路径
    printf("路线: ");
    printRoute(parent, src, dest, cities);
}

2. 网络路由

C
// OSPF 协议使用 Dijkstra 计算路由表
typedef struct {
    int network;
    int mask;
    int nextHop;
    int cost;
} RouteEntry;

void computeRoutingTable(int topology[][MAX], int routerId) {
    int dist[MAX], parent[MAX];
    dijkstra(topology, routerId, dist, parent);
    
    RouteEntry routingTable[MAX];
    int count = 0;
    
    for (int i = 0; i < MAX; i++) {
        if (i != routerId && dist[i] != INF) {
            // 找到下一跳
            int next = i;
            while (parent[next] != routerId) {
                next = parent[next];
            }
            
            routingTable[count].network = i;
            routingTable[count].nextHop = next;
            routingTable[count].cost = dist[i];
            count++;
        }
    }
}

3. 游戏寻路

C
// 使用 Dijkstra 变种进行 A* 寻路
// 结合启发式函数提高效率

typedef struct {
    int x, y;
    int g, h, f;  // g:已走距离, h:启发估计, f=g+h
    struct Node *parent;
} Node;

int heuristic(int x1, int y1, int x2, int y2) {
    // 曼哈顿距离
    return abs(x1 - x2) + abs(y1 - y2);
}

void aStar(int map[][MAX], int startX, int startY, 
           int endX, int endY) {
    // A* 是 Dijkstra 的启发式优化版本
    // 当 h=0 时退化为 Dijkstra
    // 当 h 为启发式函数时通常更快
}

4. 社交网络分析

C
// 计算用户间的最短关系链(六度分隔理论)
void analyzeSocialNetwork(int graph[][MAX], int user) {
    int dist[MAX];
    dijkstra(graph, user, dist);
    
    printf("用户 %d 的社交网络分析:\n", user);
    
    // 统计不同距离的人数
    int distance[10] = {0};
    for (int i = 0; i < MAX; i++) {
        if (dist[i] != INF && dist[i] < 10) {
            distance[dist[i]]++;
        }
    }
    
    printf("直接好友: %d 人\n", distance[1]);
    printf("二度好友: %d 人\n", distance[2]);
    printf("三度好友: %d 人\n", distance[3]);
}

复杂度总结

算法 时间复杂度 空间复杂度 最优情况
Dijkstra(朴素) O(V²) O(V) 稠密图
Dijkstra(堆优化) O((V+E)logV) O(V) 稀疏图
Bellman-Ford O(VE) O(V) 含负权边
SPFA O(kE) 平均 O(V) 随机图
Floyd-Warshall O(V³) O(V²) 多源查询

参考资料