任务描述
设计一个算法,求图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 个部分:
- 数据结构定义:用邻接矩阵存储无向图;
- 图的构建:通过输入初始化邻接矩阵;
- 最短路径求解:用 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
函数)
功能:根据输入的顶点数、边数及边的信息,初始化邻接矩阵,构建无向图。
实现步骤:
-
初始化邻接矩阵:
- 对角线元素(顶点到自身的距离)设为 0;
- 非对角线元素(顶点间无直接边)初始化为
MaxInt
(无穷大)。
-
读取边信息并更新矩阵:
- 输入每条边的起点
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=0
且m=0
时退出。
流程:
- 读取顶点数
n
和边数m
,若均为 0 则退出; - 调用
CreateGraph
构建图; - 读取源点
v0
(1-based),转换为 0-based 后调用ShortestPath_DIJ
求解。
6.关键特性与注意事项
- 无向图 :代码中边是双向的(
arcs[s-1][e-1]
和arcs[e-1][s-1]
均赋值); - 非负权重:Dijkstra 算法要求边的权重非负,代码中未做检查,但默认输入满足;
- 可达性假设 :题目中说明 "该顶点可到达其余各个顶点",因此无需处理不可达的情况(
D[v]
不会为MaxInt
); - 路径记录 :
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的图。测试样例验证了系统在无向图、带权边等场景下的正确性,满足题目要求的输入输出格式和功能需求。