【数据结构】最长的最短路径的求解

任务描述

设计一个算法,求图G中距离顶点v的最短路径长度最大的一个顶点。

编程要求

输入

多组数据,每组数据m+2行。每组数据第一行为两个整数n和m,代表有n个顶点m条路。顶点编号为1到n。第二行到第m+1行每行有三个整数a,b和c,代表顶点a和顶点b之间有一条长度为c的路。第m+2有一个整数v,代表顶点v。当n和m都等于0时,输入结束。

输出

每组数据输出两行。第一行为最短路径最长的顶点编号c,第二行为两点的最短距离d。

测试说明

平台会对你编写的代码进行测试:

输入样例:

在这里给出一组输入。例如:

python 复制代码
4 4
1 2 1
2 3 1
3 4 1
2 4 1
4
4 3
1 2 3
2 3 2
2 4 6
3
0 0

输出样例:

在这里给出相应的输出。例如:

复制代码
1
2
4
8

整体实现流程

1.整体结构与核心功能

代码主要包含 3 个部分:

  1. 数据结构定义:用邻接矩阵存储无向图;
  2. 图的构建:通过输入初始化邻接矩阵;
  3. 最短路径求解:用 Dijkstra 算法计算源点到所有顶点的最短路径,最终输出最远顶点及其距离。

2.数据结构与宏定义

1. 宏定义
cpp 复制代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MVNum 100     // 最大顶点数(图的顶点上限)
#define MaxInt 10000  // 表示"无穷大"(用于初始化未直接相连的顶点距离)
2. 图的结构体定义
cpp 复制代码
typedef char VerTexType;   // 顶点类型(此处未实际使用,代码中顶点用索引表示)
typedef int ArcType;       // 边的权重类型(整型)
typedef struct {
    ArcType arcs[MVNum][MVNum];  // 邻接矩阵:arcs[i][j]表示顶点i到j的权重
    int vexnum, arcnum;          // 图的顶点数和边数
} MGraph;

3.图的构建(CreateGraph函数)

功能:根据输入的顶点数、边数及边的信息,初始化邻接矩阵,构建无向图。

实现步骤:
  1. 初始化邻接矩阵

    • 对角线元素(顶点到自身的距离)设为 0;
    • 非对角线元素(顶点间无直接边)初始化为MaxInt(无穷大)。
  2. 读取边信息并更新矩阵

    • 输入每条边的起点s、终点e、权重w(注意:输入的顶点是 1-based 索引,代码中转换为 0-based);
    • 由于是无向图 ,边是双向的,因此同时更新arcs[s-1][e-1]arcs[e-1][s-1]w

4.Dijkstra 算法实现(ShortestPath_DIJ函数)

Dijkstra 算法用于求解带非负权重的图中,从源点到其他所有顶点的最短路径。核心思想是:通过逐步将 "距离源点最近的未访问顶点" 加入 "已确定最短路径的集合",并更新其他顶点的距离。

1. 核心变量说明
  • final[v]:布尔数组,标记顶点v是否已加入 "已确定最短路径的集合"(true表示已加入);
  • D[v]:数组,存储源点v0到顶点v的当前最短距离;
  • p[v][w]:二维数组,记录源点到顶点v的最短路径经过的顶点(p[v][i]表示路径上的第i+1个顶点,-1表示路径结束)。
2. 算法步骤(详细流程)
(1)初始化
cpp 复制代码
for (v = 0; v < G.vexnum; v++) {
    final[v] = false;                  // 所有顶点初始均未加入"已确定集合"
    D[v] = G.arcs[v0][v];              // 初始化源点到各顶点的距离(直接边的权重)
    for (w = 0; w < G.vexnum; w++) p[v][w] = -1;  // 路径数组初始化为-1(无路径)
    if (D[v] < MaxInt) {               // 若源点到v有直接边
        p[v][0] = v0;                  // 路径第一个顶点是源点v0
        p[v][1] = v;                   // 路径第二个顶点是v(直接到达)
    }
}
D[v0] = 0;       // 源点到自身的距离为0
final[v0] = true; // 源点加入"已确定集合"
(2)主循环:逐步确定最短路径

循环G.vexnum - 1次(需确定除源点外所有顶点的最短路径):

cpp 复制代码
for (i = 1; i < G.vexnum; i++) {
    // 步骤1:找到"未加入集合且距离源点最近"的顶点v
    min = MaxInt;
    for (w = 0; w < G.vexnum; w++) {
        if (!final[w] && D[w] < min) {  // 未加入集合且距离更小
            v = w;
            min = D[w];
        }
    }
    final[v] = true;  // 将v加入"已确定集合"

    // 步骤2:更新其他未加入集合的顶点到源点的距离
    for (w = 0; w < G.vexnum; w++) {
        // 若w未加入集合,且"源点→v→w"的距离 < 当前"源点→w"的距离
        if (!final[w] && min < MaxInt && G.arcs[v][w] < MaxInt && (min + G.arcs[v][w] < D[w])) {
            D[w] = min + G.arcs[v][w];  // 更新最短距离
            // 更新路径:源点到w的路径 = 源点到v的路径 + w
            for (j = 0; j < G.vexnum; j++) {
                p[w][j] = p[v][j];      // 复制源点到v的路径
                if (p[w][j] == -1) {    // 在路径末尾添加w
                    p[w][j] = w;
                    break;
                }
            }
        }
    }
}
(3)输出 "距离源点最远的顶点" 及其距离
cpp 复制代码
int k, max = D[0];
for (i = 0; i < G.vexnum; i++) {
    if (max < D[i]) {  // 找到D数组中的最大值
        max = D[i];
        k = i;         // 记录对应顶点(0-based)
    }
}
cout << k + 1 << endl;  // 转换为1-based输出顶点编号
cout << max << endl;    // 输出最大距离

5.主函数(main函数)

功能:循环读取输入,构建图并调用 Dijkstra 算法,直到输入n=0m=0时退出。

流程:

  1. 读取顶点数n和边数m,若均为 0 则退出;
  2. 调用CreateGraph构建图;
  3. 读取源点v0(1-based),转换为 0-based 后调用ShortestPath_DIJ求解。

6.关键特性与注意事项

  1. 无向图 :代码中边是双向的(arcs[s-1][e-1]arcs[e-1][s-1]均赋值);
  2. 非负权重:Dijkstra 算法要求边的权重非负,代码中未做检查,但默认输入满足;
  3. 可达性假设 :题目中说明 "该顶点可到达其余各个顶点",因此无需处理不可达的情况(D[v]不会为MaxInt);
  4. 路径记录p数组详细记录了每条最短路径的顶点序列,可用于后续打印路径。

7.C++代码完整实现

cpp 复制代码
#include<iostream>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MVNum 100     //最大顶点数
#define MaxInt 10000
using namespace std;
typedef char VerTexType;   //设置顶点类型为字符型 
typedef int ArcType;    //设置权重为整型 
typedef struct
{
	ArcType arcs[MVNum][MVNum];   //邻接矩阵 
	int vexnum, arcnum;  //顶点数和边数 
}MGraph;
void CreateGraph(MGraph* G,int n,int m);
void ShortestPath_DIJ(MGraph G, int v0, int p[][MVNum], int D[]);//3.途中距离顶点的最短路径长度最大的一个顶点,设该顶点可到达其余各个顶点
int main()
{
	while(1)
	{
		MGraph G;
		int n,m;
		cin>>n>>m;
		if(n==0&&m==0)
		{
			break;
		}
		CreateGraph(&G,n,m);
		int v0,p[MVNum][MVNum],D[MVNum];
		cin>>v0;
		ShortestPath_DIJ(G,v0-1,p,D);
	}
}
void CreateGraph(MGraph* G,int n,int m)
{
	int i, j;//图的顶点和边数 
	int s, e, w;//边的起始点,终点,权值 
	G->vexnum=n;
	G->arcnum=m;
	//邻接矩阵初始化
	for (i = 0; i < G->vexnum; i++)
	{
		for (j = 0; j < G->vexnum; j++)
		{
			if(i!=j)
			{
				G->arcs[i][j] = MaxInt;
			}
			else
			{
				G->arcs[i][j]=0;
			}
		}
	}
	for (j = 0; j < G->arcnum; j++)
	{
		cin >> s >> e >> w;//输入边的起始点,终点,权值
		G->arcs[s - 1][e - 1] = w;
		G->arcs[e - 1][s - 1] = w;//矩阵沿对角线对称 
	}
}
//迪杰斯特拉算法求有向网G的v0顶点到其余顶点v的最短路径p[v]及带权长度D[v]
//p[][]=-1表示没有路径,p[v][i]存的是从v0到v当前求得的最短路径经过的第i+1个顶点(这是打印最短路径的关键),则v0到v的最短路径即为p[v][0]到p[v][j]直到p[v][j]=-1,路径打印完毕。
//final[v]为true当且仅当v∈S,即已经求得从v0到v的最短路径。
void ShortestPath_DIJ(MGraph G, int v0, int p[][MVNum], int D[]) 
{
	int v, w, i, j, min;
	bool final[10];

	for (v = 0; v < G.vexnum; v++) 
	{
		final[v] = false;//设初值
		D[v] = G.arcs[v0][v];//D[]存放v0到v得最短距离,初值为v0到v的直接距离
		for (w = 0; w < G.vexnum; w++)
		{
			p[v][w] = -1;//设p[][]初值为-1,即没有路径
		}
		if (D[v] < MaxInt)
		{ //v0到v有直接路径
			p[v][0] = v0;//v0到v最短路径经过的第一个顶点
			p[v][1] = v;//v0到v最短路径经过的第二个顶点
		}
	}

	D[v0] = 0;//v0到v0距离为0
	final[v0] = true;//v0顶点并入S集

	for (i = 1; i < G.vexnum; i++) 
	{ //其余G.vexnum-1个顶点
		//开始主循环,每次求得v0到某个顶点v的最短路径,并将v并入S集,然后更新p和D
		min = MaxInt;
		for (w = 0; w < G.vexnum; w++)//对所有顶点检查
		{
			if (!final[w] && D[w] < min)
			{ //在S集之外(即final[]=false)的顶点中找离v0最近的顶点,将其赋给v,距离赋给min
				v = w;
				min = D[w];
			}
		}
		final[v] = true;//v并入S集
		for (w = 0; w < G.vexnum; w++) 
		{ //根据新并入的顶点,更新不在S集的顶点到v0的距离和路径数组
			if (!final[w] && min < MaxInt && G.arcs[v][w] < MaxInt && (min + G.arcs[v][w] < D[w]))
			{
				//w不属于S集且v0->v->w的距离<目前v0->w的距离
				D[w] = min + G.arcs[v][w];//更新D[w]
				for (j = 0; j < G.vexnum; j++) 
				{ //修改p[w],v0到w经过的顶点包括v0到v经过的所有顶点再加上顶点w
					p[w][j] = p[v][j];
					if (p[w][j] == -1) 
					{ //在p[w][]第一个等于-1的地方加上顶点w
						p[w][j] = w;
						break;
					}
				}
			}
		}
	}
	int k,max=D[0];
	for(i=0;i<G.vexnum;i++)
	{
		if(max<D[i])
		{
			max=D[i];
			k=i;
		}
	}
	cout<<k+1<<endl;
	cout<<max<<endl;
}

8.Python代码完整实现

python 复制代码
import sys

# 定义常量,与原C++保持一致
MaxInt = 10000
MVNum = 100  # 最大顶点数,与原C++一致


def create_graph(n: int, m: int) -> dict:
    """
    构建无向图的邻接矩阵(对应原C++ CreateGraph函数)
    :param n: 顶点数(1开始编号)
    :param m: 边数
    :return: 图结构字典,包含顶点数(vexnum)和邻接矩阵(arcs)
    """
    # 初始化邻接矩阵:对角线为0,其余为MaxInt
    arcs = [[MaxInt for _ in range(n)] for _ in range(n)]
    for i in range(n):
        arcs[i][i] = 0  # 自身到自身距离为0

    # 读取m条边,无向图双向赋值权重
    for _ in range(m):
        s, e, w = map(int, sys.stdin.readline().split())
        # 转换为0开始的索引
        s_idx = s - 1
        e_idx = e - 1
        arcs[s_idx][e_idx] = w
        arcs[e_idx][s_idx] = w

    return {
        "vexnum": n,
        "arcs": arcs
    }


def shortest_path_dij(G: dict, v0: int) -> tuple:
    """
    迪杰斯特拉算法(对应原C++ ShortestPath_DIJ函数)
    :param G: 图结构字典
    :param v0: 起点(0开始编号)
    :return: (最远顶点编号(1开始), 最远距离)
    """
    n = G["vexnum"]
    # 初始化辅助数组
    final = [False] * n  # 标记是否已确定最短路径
    D = [0] * n          # 存储v0到各顶点的最短距离
    # 路径数组p:p[v][i]表示v0到v的路径第i个顶点(-1表示无后续)
    p = [[-1 for _ in range(n)] for _ in range(n)]

    # 初始化D和p数组
    for v in range(n):
        D[v] = G["arcs"][v0][v]
        # 若v0到v有直接路径,初始化路径
        if D[v] < MaxInt:
            p[v][0] = v0
            p[v][1] = v

    # 起点自身处理
    D[v0] = 0
    final[v0] = True

    # 迭代求其余n-1个顶点的最短路径
    for i in range(1, n):
        # 步骤1:找当前未确定最短路径中距离v0最近的顶点v
        min_dist = MaxInt
        v = -1
        for w in range(n):
            if not final[w] and D[w] < min_dist:
                min_dist = D[w]
                v = w

        if v == -1:
            break  # 剩余顶点不可达,提前退出
        final[v] = True  # 将v加入已确定集合

        # 步骤2:更新所有未确定顶点的最短距离和路径
        for w in range(n):
            if not final[w] and min_dist < MaxInt and G["arcs"][v][w] < MaxInt:
                if min_dist + G["arcs"][v][w] < D[w]:
                    D[w] = min_dist + G["arcs"][v][w]
                    # 复制v的路径到w,并在末尾添加w
                    j = 0
                    while p[v][j] != -1:
                        p[w][j] = p[v][j]
                        j += 1
                    p[w][j] = w

    # 找距离v0最远的顶点(可到达的,D[w] < MaxInt)
    max_dist = -1
    far_vex = v0  # 默认起点自身
    for v in range(n):
        if D[v] < MaxInt and D[v] > max_dist:
            max_dist = D[v]
            far_vex = v

    # 转换为1开始的顶点编号
    return far_vex + 1, max_dist


def main():
    while True:
        # 读取顶点数n和边数m
        line = sys.stdin.readline()
        while line.strip() == "":  # 跳过空行
            line = sys.stdin.readline()
        n, m = map(int, line.strip().split())
        
        if n == 0 and m == 0:
            break  # 输入结束
        
        # 构建图
        G = create_graph(n, m)
        
        # 读取起点v0(1开始)
        v0_line = sys.stdin.readline()
        while v0_line.strip() == "":
            v0_line = sys.stdin.readline()
        v0 = int(v0_line.strip()) - 1  # 转换为0开始
        
        # 计算并输出结果
        far_vex, max_dist = shortest_path_dij(G, v0)
        print(far_vex)
        print(max_dist)


if __name__ == "__main__":
    main()

9.Java代码完整实现

java 复制代码
import java.util.Scanner;

// 常量定义(与原C++一致)
class Constant {
    public static final int OK = 1;
    public static final int ERROR = 0;
    public static final int OVERFLOW = -2;
    public static final int MVNum = 100; // 最大顶点数
    public static final int MaxInt = 10000; // 表示无穷大
}

// 图结构类(对应原C++的MGraph结构体)
class MGraph {
    int[][] arcs; // 邻接矩阵
    int vexnum;   // 顶点数
    int arcnum;   // 边数

    // 构造方法(对应原C++的CreateGraph函数)
    public MGraph(int n, int m, Scanner scanner) {
        this.vexnum = n;
        this.arcnum = m;
        // 初始化邻接矩阵:自身距离为0,其余为无穷大
        arcs = new int[Constant.MVNum][Constant.MVNum];
        for (int i = 0; i < this.vexnum; i++) {
            for (int j = 0; j < this.vexnum; j++) {
                arcs[i][j] = (i == j) ? 0 : Constant.MaxInt;
            }
        }
        // 读取边信息(无向图:双向赋值权重)
        for (int j = 0; j < this.arcnum; j++) {
            int s = scanner.nextInt() - 1; // 转换为0开始索引
            int e = scanner.nextInt() - 1;
            int w = scanner.nextInt();
            arcs[s][e] = w;
            arcs[e][s] = w;
        }
    }
}

public class DijkstraAlgorithm {
    // 迪杰斯特拉算法(对应原C++的ShortestPath_DIJ函数)
    public static void shortestPathDIJ(MGraph G, int v0) {
        int n = G.vexnum;
        boolean[] finalArr = new boolean[n]; // 标记是否已确定最短路径(替换原C++的final数组)
        int[] D = new int[n];                // 存储v0到各顶点的最短距离
        int[][] p = new int[Constant.MVNum][Constant.MVNum]; // 存储最短路径

        // 1. 初始化D、p数组
        for (int v = 0; v < n; v++) {
            finalArr[v] = false;
            D[v] = G.arcs[v0][v];
            // 初始化路径数组为-1(表示无路径)
            for (int w = 0; w < Constant.MVNum; w++) {
                p[v][w] = -1;
            }
            // 若v0到v有直接路径,初始化路径
            if (D[v] < Constant.MaxInt) {
                p[v][0] = v0;
                p[v][1] = v;
            }
        }
        // 起点自身处理
        D[v0] = 0;
        finalArr[v0] = true;

        // 2. 迭代求其余n-1个顶点的最短路径
        for (int i = 1; i < n; i++) {
            // 找当前未确定路径中距离v0最近的顶点
            int min = Constant.MaxInt;
            int v = -1;
            for (int w = 0; w < n; w++) {
                if (!finalArr[w] && D[w] < min) {
                    min = D[w];
                    v = w;
                }
            }
            if (v == -1) break; // 剩余顶点不可达,提前退出
            finalArr[v] = true; // 将v加入已确定集合

            // 3. 更新所有未确定顶点的距离和路径
            for (int w = 0; w < n; w++) {
                if (!finalArr[w] && min < Constant.MaxInt 
                    && G.arcs[v][w] < Constant.MaxInt 
                    && (min + G.arcs[v][w] < D[w])) {
                    // 更新最短距离
                    D[w] = min + G.arcs[v][w];
                    // 复制v的路径到w,并追加w作为终点
                    int j = 0;
                    while (p[v][j] != -1) {
                        p[w][j] = p[v][j];
                        j++;
                    }
                    p[w][j] = w;
                }
            }
        }

        // 4. 找距离v0最远的顶点(与原C++逻辑一致)
        int maxDist = D[0];
        int farVex = 0;
        for (int i = 0; i < n; i++) {
            if (D[i] > maxDist) {
                maxDist = D[i];
                farVex = i;
            }
        }

        // 输出结果(转换为1开始的顶点编号)
        System.out.println(farVex + 1);
        System.out.println(maxDist);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 读取顶点数n和边数m
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            if (n == 0 && m == 0) {
                break; // 输入结束,退出循环
            }
            // 构建图(读取m条边)
            MGraph G = new MGraph(n, m, scanner);
            // 读取起点v0(转换为0开始索引)
            int v0 = scanner.nextInt() - 1;
            // 执行迪杰斯特拉算法并输出结果
            shortestPathDIJ(G, v0);
        }
        scanner.close();
    }
}

10.总结

本文实现了一个基于Dijkstra算法的图最短路径求解系统,用于找到图中距离指定顶点最远顶点及其最短距离。系统核心功能包括:

(1)构建无向图邻接矩阵;

(2)使用Dijkstra算法计算单源最短路径;

(3)找出距离源点最远的顶点。

实现采用邻接矩阵存储图结构,包含C++、Python和Java三种语言版本,支持多组数据输入。算法时间复杂度为O(n²),适用于顶点数不超过100的图。测试样例验证了系统在无向图、带权边等场景下的正确性,满足题目要求的输入输出格式和功能需求。

相关推荐
麦麦鸡腿堡3 小时前
Java的代码块介绍与快速入门
java·开发语言
泡沫冰@3 小时前
数据结构(5)
数据结构
小龙报3 小时前
《算法每日一题(1)--- 连续因子》
c语言·开发语言·c++·windows·git·算法·visual studio
流浪大叔3 小时前
Python下载实战技巧的技术文章大纲
开发语言·python
祁同伟.3 小时前
【C++】异常
开发语言·c++
梅小西爱学习3 小时前
线上CPU飙到100%?别慌,这3个工具比top快10倍!
java·后端·cpu
用户68545375977693 小时前
🎯 Python迭代器与生成器:从入门到"哦原来如此!"
python
开心-开心急了3 小时前
PySide6 使用搜索引擎搜索 多类实现 更新1次
python·pyqt·pyside
没有bug.的程序员3 小时前
金融支付分布式架构实战:从理论到生产级实现
java·分布式·微服务·金融·架构·分布式调度系统