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

任务描述

设计一个算法,求图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的图。测试样例验证了系统在无向图、带权边等场景下的正确性,满足题目要求的输入输出格式和功能需求。

相关推荐
Lhappy嘻嘻13 小时前
Java IO|File 文件操作 + 字节流 / 字符流完整笔记 + 递归删除文件实战
java·笔记·php
伊玛目的门徒14 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
Jerry14 小时前
LeetCode 226. 翻转二叉树
算法
想做小南娘,发现自己是女生喵15 小时前
【无标题】
数据结构·算法
向日的葵00616 小时前
langchain的Tools教程(三)
python·langchain·tools
懒鸟一枚17 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
言乐617 小时前
Python实现可运行解密游戏游戏框架
python·游戏·小程序·游戏程序·关卡设计
Kx_Triumphs17 小时前
HDU4348 To the moon(主席树区间修改模板)
算法·题解
YUS云生17 小时前
Python学习笔记·第31天:FastAPI入门——路由、路径参数、查询参数与请求体
笔记·python·学习