邻接表实现的Dijkstra算法

本文详细介绍了如何使用邻接表和优先队列优化Dijkstra算法,从文件中读取图数据,计算源点到所有顶点的最短路径。通过C++代码实现,包括邻接表结构体定义、Dijkstra算法的执行流程以及文件读取。程序允许用户输入源点,输出最短路径及其距离。

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

一、算法目的

  1. 使用邻接表实现Dijkstra算法
  2. 使用priority_queue(堆)优化取最小权值边的时间复杂度
  3. 从文件中读取的数据格式如下:
    在这里插入图片描述

二、算法代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <string>

#define maxnum 201      // 因为结点序号是从1开始的,因此设置为201,使得数组下标可以是1-200
#define edgenum 3734    // 总共有3734条边,可以由从文件读取数据的算法计算得到
#define INF 1000000

using namespace std;

typedef char VertexType;

//边结点结构体
typedef struct ArcNode
{
    int adjvex;
    int weight;
    struct ArcNode* nextarc;
}ArcNode;

//顶点结构体
typedef struct VNode
{
    VertexType data;
    ArcNode* firstarc;
}VNode, AdjList[maxnum];

//邻接表
typedef struct
{
    AdjList vertices;
    int vexnum, arcnum;
}ALGraph;

//顶点节点,保存id和到源顶点的估算距离,优先队列需要的类型
struct Node
{
    int id;     //从源到目标顶点id
    int w;      //从源到id的距离

    //因要实现最小堆,按升序排列,因而需要重载运算符,重定义优先级,使得结点升序
    friend bool operator < (struct Node a, struct Node b)
    {
        return a.w > b.w;
    }
};

int path[maxnum];
int visited[maxnum] = { 0 };
Node dist[maxnum];
priority_queue<Node>q;

void Dijkstra(ALGraph g, int v0, int n)
{

    for (int i = 1; i <= n; i++)
    {
        dist[i].id = i;     //从源v0到结点i
        dist[i].w = INF;    //初始距离为INF
        path[i] = -1;       //初始最短路径的前一个结点为-1
        visited[i] = 0;     //初始为所有结点都没有访问过
    }
    dist[v0].w = 0;
    q.push(dist[v0]);       //将源点入优先队列
    while (!q.empty())
    {
        Node node = q.top();  //取得当前权值w最小值对应的结点
        q.pop();
        int u = node.id;

        if (visited[u])     //若当前结点还没有访问过
            continue;
        visited[u] = 1;
        ArcNode* p = g.vertices[u].firstarc;    //取得当前结点对应的第一条边

        while (p)
        {
            int tempv = p->adjvex;
            int tempw = p->weight;

            //若该边对应的另外一个结点还没有访问过并且源点到u加上u到tempv的权值小于源点直接到tempv的权值,更新权值
            if (!visited[tempv] && dist[tempv].w > dist[u].w + tempw)
            {
                dist[tempv].w = dist[u].w + tempw;
                path[tempv] = u;        // 更新源点到tempv的最短路径的tempv前一结点为u
                q.push(dist[tempv]);    
            }
            p = p->nextarc;
        }
    }
}

void CreateALGraph(ALGraph& g, int arc, int vex)
{
    ifstream fp;
    int source;
    int n, w;
    fp.open("./data.txt", ios::in);
    if (!fp.is_open()) {
        cout << "打开文件失败!!" << endl;
        return;
    }
    string line;
    int count = 0;

    g.arcnum = arc;
    g.vexnum = vex;
    int i;

    for (i = 1; i <= vex; i++)
    {
        g.vertices[i].firstarc = NULL;
    }

    while (1)
    {

        fp >> source;
        //if (source != 200)
          //  cout << source << endl;

        if (!getline(fp, line)) break;
        //cout << line;
        stringstream ss(line); // 读取一行文件,将其当作输入流输入
        string token;

        while (ss >> token)
        {
            n = stoi(token);
            if (ss >> token) {
                w = stoi(token);
            }

            ArcNode* q = (ArcNode*)malloc(sizeof(ArcNode));
            q->adjvex = n;
            q->weight = w;

            q->nextarc = g.vertices[source].firstarc;
            g.vertices[source].firstarc = q;

            //cout << n << "," << w << endl;
            //count++; //计算边数

        }
    }

    fp.close();
}

int main()
{
    int m, n;
    //顶点,边
    n = 200;
    m = edgenum;
    //cin >> n >> m;
    ALGraph g;
    CreateALGraph(g, m, n);
    //cout << g.vertices[1].firstarc->nextarc->adjvex <<g.vertices[1].firstarc->nextarc->weight << endl;
    //cout << g.vertices[130].firstarc->nextarc->adjvex << g.vertices[130].firstarc->nextarc->weight << endl;
    //cout << g.vertices[200].firstarc->adjvex << g.vertices[200].firstarc->weight << endl;
    int temp;

    int j;
    while (1) {
        cout << "请输入源点(输入-1结束程序):";
        cin >> j;
        if (j > 200 || j < 1) {
            cout << "源点只能设置为1-200!" << endl;
            continue;
        }
        if (j == -1)break;
        
        cout << "******设置源点为 " << j << " ******" <<endl;
        Dijkstra(g, j, n);
        for (int i = 1; i <= n; i++ ) {
            temp = i;
            if (dist[i].w != INF) {
                cout << "源点" << j << "到" << i <<"的最短路径为:" << dist[i].w << endl;
                cout << "其路径为:" << temp;
                while (path[temp] != -1) {
                    cout << " <- " << path[temp];
                    temp = path[temp];
                }
                cout << endl;
            }
            else
                cout << "源点" << j << "到" << i << "没有路径" << endl;
            cout << endl;
        }
        cout << "*************************" << endl << endl;

    }
    return 0;
}

三、运行效果
可以输入源点,计算不同源点到其余所有顶点的最短路径距离和经过的最短路径
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值