Dijkstra算法用于求解单源最短路径问题,即从一个起始节点到图中其他所有节点的最短路径

  1. 使用邻接矩阵: 邻接表在内存管理上更灵活,但会增加代码行数。邻接矩阵更简洁,但空间复杂度更高。对于小型图,这是可以接受的。

  2. 省略错误处理: 为了精简代码,我们省略了内存分配错误的检查。 实际应用中,这非常重要,但在教学示例中可以忽略。

  3. 简化输出: 只输出源点到其他节点的最短距离,不输出路径。

以下是一个简化的Dijkstra算法C代码,使用邻接矩阵,大约20行:

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

#define INF INT_MAX
#define V 5 // 顶点数,需要修改

int main() {
    int graph[V][V] = {
        {0, 4, 1, INF, INF},
        {4, 0, 2, 1, INF},
        {1, 2, 0, 5, INF},
        {INF, 1, 5, 0, 3},
        {INF, INF, INF, 3, 0}
    }; // 邻接矩阵表示图

    int dist[V];
    int visited[V] = {0};
    int src = 0;

    for (int i = 0; i < V; i++) dist[i] = INF;
    dist[src] = 0;

    for (int count = 0; count < V - 1; count++) {
        int u = -1;
        for (int v = 0; v < V; v++)
            if (!visited[v] && (u == -1 || dist[v] < dist[u])) u = v;
        visited[u] = 1;
        for (int v = 0; v < V; v++)
            if (!visited[v] && graph[u][v] && dist[u] != INF && dist[u] + graph[u][v] < dist[v])
                dist[v] = dist[u] + graph[u][v];
    }

    for (int i = 0; i < V; i++)
        printf("从 %d 到 %d 的最短距离:%d\n", src, i, dist[i] == INF ? -1 : dist[i]);
    return 0;
}

注意: 这个简化版本有以下限制:

  • 固定顶点数: V 定义了顶点数,需要手动修改。
  • 硬编码图: 图的邻接矩阵直接写在了代码里,修改图需要修改代码。
  • 无错误处理: 没有处理内存分配错误或图结构错误。
  • 仅输出距离: 只输出最短距离,不输出路径。

它适用于边权重非负的图。对于更复杂的应用,建议使用更健壮的版本(下面提供的版本)。 这个简化版仅用于理解算法的核心思想。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h> // 用于INT_MAX

#define INF INT_MAX // 定义无穷大

// 表示图中边的结构体
struct Edge {
    int dest;       // 目标节点
    int weight;     // 权重
};

// 表示图中节点的结构体
struct Node {
    int vertex;     // 节点编号
    struct Edge* edges; // 指向边的数组
    int edgeCount;  // 边数
};

// 创建图的函数,numVertices为节点数,numEdges为边数
struct Node* createGraph(int numVertices, int numEdges) {
    struct Node* graph = (struct Node*)malloc(sizeof(struct Node) * numVertices);
    if (graph == NULL) {
        perror("内存分配失败");
        exit(1);
    }
    for (int i = 0; i < numVertices; i++) {
        graph[i].vertex = i;
        graph[i].edges = NULL;
        graph[i].edgeCount = 0;
    }

    // 添加边 (需要根据你的图的表示方式修改这部分)
    // 例如:添加从节点0到节点1,权重为4的边
    // graph[0].edges = addEdge(graph[0].edges, &(graph[0].edgeCount), 1, 4);
    // ... 添加其他边 ...
    return graph;
}

//辅助函数,向节点添加一条边
struct Edge* addEdge(struct Edge* edges, int *edgeCount, int dest, int weight){
    struct Edge* newEdges = (struct Edge*)realloc(edges, sizeof(struct Edge) * (*edgeCount + 1));
    if(newEdges == NULL){
        perror("内存分配失败");
        exit(1);
    }
    newEdges[*edgeCount].dest = dest;
    newEdges[*edgeCount].weight = weight;
    (*edgeCount)++;
    return newEdges;
}

// Dijkstra算法实现
void dijkstra(struct Node* graph, int numVertices, int source) {
    int dist[numVertices]; // 从源节点到每个节点的距离
    int visited[numVertices]; // 用于标记节点是否已访问
    int minDistance;
    int minVertex;

    // 初始化距离和访问数组
    for (int i = 0; i < numVertices; i++) {
        dist[i] = INF;
        visited[i] = 0;
    }
    dist[source] = 0; // 源节点到自身的距离为0

    // 寻找所有节点的最短路径
    for (int count = 0; count < numVertices - 1; count++) {
        minDistance = INF;
        minVertex = -1;

        // 找到距离最短且未访问的节点
        for (int v = 0; v < numVertices; v++) {
            if (!visited[v] && dist[v] <= minDistance) {
                minDistance = dist[v];
                minVertex = v;
            }
        }

        // 如果没有未访问的节点,则跳出循环
        if (minVertex == -1) break;

        // 将选定的节点标记为已访问
        visited[minVertex] = 1;

        // 更新相邻节点的距离
        for (int i = 0; i < graph[minVertex].edgeCount; i++) {
            int v = graph[minVertex].edges[i].dest;
            int weight = graph[minVertex].edges[i].weight;
            if (!visited[v] && dist[minVertex] != INF && dist[minVertex] + weight < dist[v]) {
                dist[v] = dist[minVertex] + weight;
            }
        }
    }

    // 打印从源节点到其他节点的最短距离
    printf("从节点 %d 到其他节点的最短距离:\n", source);
    for (int i = 0; i < numVertices; i++) {
        printf("节点 %d: %d\n", i, dist[i] == INF ? -1 : dist[i]); // -1表示不可达
    }
}

int main() {
    int numVertices = 5;  // 例子:5个节点
    int numEdges = 7;     // 例子:7条边

    // 创建图 (需要根据你的图的实际情况修改)
    struct Node* graph = createGraph(numVertices, numEdges);

    // 添加边 (例子,需要根据你的图的实际情况修改)
    graph[0].edges = addEdge(graph[0].edges, &(graph[0].edgeCount), 1, 4);
    graph[0].edges = addEdge(graph[0].edges, &(graph[0].edgeCount), 2, 1);
    graph[1].edges = addEdge(graph[1].edges, &(graph[1].edgeCount), 3, 1);
    graph[2].edges = addEdge(graph[2].edges, &(graph[2].edgeCount), 1, 2);
    graph[2].edges = addEdge(graph[2].edges, &(graph[2].edgeCount), 3, 5);
    graph[3].edges = addEdge(graph[3].edges, &(graph[3].edgeCount), 4, 3);
    graph[4].edges = addEdge(graph[4].edges, &(graph[4].edgeCount), 3, 2);


    int sourceVertex = 0; // 例子:源节点为0

    dijkstra(graph, numVertices, sourceVertex);

    //释放动态分配的内存,避免内存泄漏
    for(int i=0; i<numVertices; ++i){
        free(graph[i].edges);
    }
    free(graph);

    return 0;
}

使用说明:

  1. 修改createGraph函数和addEdge函数: 代码中createGraph函数和addEdge函数中的注释部分需要根据你图的实际结构进行修改。 你需要根据你的图的表示方法(邻接矩阵或邻接表)来修改代码。 此例子使用邻接表。

  2. 编译和运行: 使用C编译器(例如GCC)编译代码:gcc dijkstra.c -o dijkstra,然后运行:./dijkstra

这段代码添加了更完善的内存错误检查和内存释放,避免了内存泄漏的问题。 请务必根据你自己的图的结构修改createGraph和添加边的部分。 本例使用邻接表表示图,你可以根据需要修改为邻接矩阵。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐