通信网中的算法问题 实验三 最短路径算法实验

实验三 通信网中的算法问题—最短路径算法实验

一. 引言

  1. 单源最短路问题: 给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为源。要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。这个问题通常称为单源最短路径问题。

  2. 基本Dijkstra算法基本思想:

*1.*通过Dijkstra计算图G中的最短路径时,需要指定一个起点D(即从顶点D开始计算)。

*2.*此外,引进两个数组S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点D的距离)。

*3.*初始时,数组S中只有起点D;数组U中是除起点D之外的顶点,并且数组U中记录各顶点到起点D的距离。如果顶点与起点D不相邻,距离为无穷大。

*4.*然后,从数组U中找出路径最短的顶点K,并将其加入到数组S中;同时,从数组U中移除顶点K。接着,更新数组U中的各顶点到起点D的距离。

*5.*重复第4步操作,直到遍历完所有顶点。

二. 系统设计要求

实验要求:

  1. 给定任意带权重的连通图形,求出指定起点到所有点的最短路径和最短路径的长度,并打印出经过的路径及最短路径长度。

  2. 请把图形转化为数据,并通过文本/手动输入。

打印显示要求:

  1. 要求打印所有节点信息:

​ 例如:节点1:重庆;

​ 节点2:**;

​ **********;

  1. 要求打印所有边的信息:

​ 例如:节点* 节点* 路径长度*

  1. 最短路径信息:

​ 源节点到节点的最短路径长度*;

​ 必经节点**。。。。。。;

三. 设计思路与方案

1.程序流程图设计

在这里插入图片描述

2.全局变量及结构体

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

#define CITY_NUM 8
#define unconnect INT_MAX // 表示无连接
int graph[CITY_NUM][CITY_NUM]; // 城市之间的距离
int d[CITY_NUM]; // 距离数组,存储从起点到各顶点的最短距离
int p[CITY_NUM]; // 前驱节点数组,存储最短路径的前驱节点
int S[CITY_NUM]; // 标记已访问的顶点
char cities[CITY_NUM][20] = {
    "重庆",
    "北京",
    "成都",
    "上海",
    "深圳",
    "杭州",
    "广州",
    "武汉"
};

3.算法设计

3.1整体代码
#include <stdio.h>
#include <string.h>
#include <limits.h>

#define CITY_NUM 8
#define unconnect INT_MAX // 表示无连接
int graph[CITY_NUM][CITY_NUM]; // 城市之间的距离
int d[CITY_NUM]; // 距离数组,存储从起点到各顶点的最短距离
int p[CITY_NUM]; // 前驱节点数组,存储最短路径的前驱节点
int S[CITY_NUM]; // 标记已访问的顶点
char cities[CITY_NUM][20] = {
    "重庆",
    "北京",
    "成都",
    "上海",
    "深圳",
    "杭州",
    "广州",
    "武汉"
};

void fileop(void)//读文件构造图
{
    FILE *fp;
    int i, j;
    int dis;
    int from, to;
    if ((fp = fopen("Dijkstra_input.txt", "r")) == NULL)
    {
        printf("文件打开失败");
    }
    else
    {
        for (i = 0; i < CITY_NUM; i++)
        {
            for (j = 0; j < CITY_NUM; j++)
            {
                graph[i][j] = unconnect;
            }
        }

        while (fscanf(fp, "%d %d %d", &from, &to, &dis) == 3)
        {
            graph[from - 1][to - 1] = dis;
            graph[to - 1][from - 1] = dis;
        }
        fclose(fp);
    }
}

// 更新距离数组和前驱节点数组
void Update(int i)
{
    for (int j = 0; j < CITY_NUM; j++)
    {
        if (!S[j] && graph[i][j] != unconnect)
        {
            int newDist = d[i] + graph[i][j];
            if (newDist < d[j])
            {
                d[j] = newDist;
                p[j] = i;
            }
        }
    }
}

// 查找未访问顶点中距离最小的顶点
int FindMin()
{
    int minDist = INT_MAX;
    int minIndex = -1; // 记录未访问的顶点中距离最小的顶点
    for (int i = 0; i < CITY_NUM; i++)
    {
        if (!S[i] && d[i] < minDist)
        {
            minDist = d[i];
            minIndex = i;
        }
    }
    return minIndex;
}

void DijkstraAlg(int s)
{
    for (int i = 0; i < CITY_NUM; i++)
    {
        d[i] = INT_MAX; // 初始化距离为无穷大
        p[i] = -1; // 初始化前驱节点为无效值
        S[i] = 0; // 初始化未访问
    }

    d[s] = 0;
    p[s] = -1;

    while (1)
    {
        int i = FindMin();
        if (i == -1)
        {
            break; // 所有顶点都已访问完毕
        }

        S[i] = 1; // 标记顶点i为已访问

        Update(i);
    }
}
void printCities()
{
    printf("所有节点信息:\n");
    for (int i = 0; i < CITY_NUM; i++)
    {
        printf("节点%d:%s;\n", i + 1, cities[i]);
    }
    printf("\n");
}

void printEdges()
{
    printf("所有边的信息:\n");
    for (int i = 0; i < CITY_NUM; i++)
    {
        for (int j = i + 1; j < CITY_NUM; j++)
        {
            if (graph[i][j] != unconnect)
            {
                printf("节点%d 到 节点%d 的路径长度:%d;\n", i + 1, j + 1, graph[i][j]);
            }
        }
    }
    printf("\n");
}
void printShortestPaths(int source)
{
    printf("从节点%d到各个节点的最短路径长度和必经节点:\n", source + 1);
    for (int i = 0; i < CITY_NUM; i++)
    {
        if (i != source)
        {
            printf("到节点%d的最短距离:%d\n", i + 1, d[i]);
            printf("必经节点:");
            int path[CITY_NUM]; // 用于存储路径
            int current = i;
            int pathLength = 0;

            // 反向遍历前驱节点数组,将路径存入 path 数组
            while (current != -1)
            {
                path[pathLength++] = current;
                current = p[current];
            }

            // 打印路径,从起点到终点
            for (int j = pathLength - 1; j >= 0; j--)
            {
                printf("%s", cities[path[j]]);
                if (j > 0)
                {
                    printf(" -> ");
                }
            }
            printf("\n");
        }
    }
}
int main()
{
    int ver1; // 定义起始节点
    fileop();
    printf("请输入起始节点(1~8):\n");
    scanf("%d", &ver1);
    if (ver1 < 1 || ver1 > CITY_NUM)
    {
        printf("无效的起始节点编号\n");
        return 1;
    }
    DijkstraAlg(ver1 - 1);
    printCities();
    printEdges();
    printShortestPaths(ver1 - 1);
    return 0;
}

3.2 文件读取及图的构建
void fileop(void)//读文件构造图
{
    FILE *fp;
    int i, j;
    int dis;
    int from, to;
    if ((fp = fopen("Dijkstra_input.txt", "r")) == NULL)
    {
        printf("文件打开失败");
    }
    else
    {
        //初始化图,将每个节点之间权值设为无穷大表示为未连接
        for (i = 0; i < CITY_NUM; i++)
        {
            for (j = 0; j < CITY_NUM; j++)
            {
                graph[i][j] = unconnect;
            }
        }
        //读取文件节点之间的权值并赋值
        while (fscanf(fp, "%d %d %d", &from, &to, &dis) == 3)
        {
            graph[from - 1][to - 1] = dis;
            graph[to - 1][from - 1] = dis;
        }
        fclose(fp);
    }
}
3.3 Dijsktra算法

Dijkstra算法的主要步骤,包括初始化、选取距离最小的未访问节点、标记已访问、更新距离和前驱节点数组,从而找到从起点到各个节点的最短路径

// 查找未访问顶点中距离最小的顶点
int FindMin()
{
    int minDist = INT_MAX;
    int minIndex = -1; // 记录未访问的顶点中距离最小的顶点
    for (int i = 0; i < CITY_NUM; i++)
    {
        if (!S[i] && d[i] < minDist)
        {
            minDist = d[i];
            minIndex = i;
        }
    }
    return minIndex;
}
// 更新距离数组和前驱节点数组
void Update(int i)
{
    for (int j = 0; j < CITY_NUM; j++)
    {
        //若找到未访问节点
        if (!S[j] && graph[i][j] != unconnect)
        {
            int newDist = d[i] + graph[i][j];
            if (newDist < d[j])
            {
                d[j] = newDist;
                p[j] = i;
            }
        }
    }
}

void DijkstraAlg(int s)
{
    for (int i = 0; i < CITY_NUM; i++)
    {
        d[i] = INT_MAX; // 初始化距离为无穷大
        p[i] = -1; // 初始化前驱节点为无效值
        S[i] = 0; // 初始化未访问
    }

    d[s] = 0;
    p[s] = -1;

    while (1)
    {
        int i = FindMin();
        if (i == -1)//是否还有未访问的节点
        {
            break; // 所有顶点都已访问完毕
        }

        S[i] = 1; // 标记顶点i为已访问

        Update(i);
    }
}
3.4打印显示信息

printShortestPaths函数中,要实现源节点到目标节点的顺序打印必经路径,由于前驱节点数组 p 存储的是从源节点到每个节点的最短路径上的前一个节点,也就是说,p[i] 存储了从源节点到节点 i 的最短路径上的前一个节点的索引。

所以需要通过逆序遍历前驱节点数组 p,从目标节点开始向源节点回溯,构建完整的最短路径。

void printCities()
{
    printf("所有节点信息:\n");
    for (int i = 0; i < CITY_NUM; i++)
    {
        printf("节点%d:%s;\n", i + 1, cities[i]);
    }
    printf("\n");
}

void printEdges()
{
    printf("所有边的信息:\n");
    for (int i = 0; i < CITY_NUM; i++)
    {
        for (int j = i + 1; j < CITY_NUM; j++)
        {
            if (graph[i][j] != unconnect)
            {
                printf("节点%d 到 节点%d 的路径长度:%d;\n", i + 1, j + 1, graph[i][j]);
            }
        }
    }
    printf("\n");
}
void printShortestPaths(int source)
{
    printf("从节点%d到各个节点的最短路径长度和必经节点:\n", source + 1);
    for (int i = 0; i < CITY_NUM; i++)
    {
        if (i != source)
        {
            printf("到节点%d的最短距离:%d\n", i + 1, d[i]);
            printf("必经节点:");
            int path[CITY_NUM]; // 用于存储路径
            int current = i;
            int pathLength = 0;

            // 反向遍历前驱节点数组,将路径存入 path 数组
            while (current != -1)
            {
                path[pathLength++] = current;
                current = p[current];
            }

            // 打印路径,从起点到终点
            for (int j = pathLength - 1; j >= 0; j--)
            {
                printf("%s", cities[path[j]]);
                if (j > 0)
                {
                    printf(" -> ");
                }
            }
            printf("\n");
        }
    }
}
3.5 主函数
int main()
{
    int ver1; // 定义起始节点
    fileop();
    printf("请输入起始节点(1~8):\n");
    scanf("%d", &ver1);
    if (ver1 < 1 || ver1 > CITY_NUM)
    {
        printf("无效的起始节点编号\n");
        return 1;
    }
    DijkstraAlg(ver1 - 1);
    printCities();
    printEdges();
    printShortestPaths(ver1 - 1);
    return 0;
}

四.调试

首先测试读文件是否正常

在这里插入图片描述

打印结果丢头重尾,在循环中找到错误部分并修正

在这里插入图片描述

读文件无误后进行下一步,构建图结构

在这里插入图片描述

成功构建图结构,继续Dijsktra算法的编写和调试

五.心得体会

将程序分解成多个模块或函数,每个模块负责不同的功能。这使得代码更易于维护和理解。编写程序过程中还需掌握一些基本的异常处理,如文件打开失败或无效输入等,这提高了程序的健壮性。

六.意见

七.附录

结果展示
在这里插入图片描述

在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值