“若离本枝,一日面色变,二日而香变,三日而味变,四五日外,色香味尽去矣。”唐朝的都城却在西安,离最近的荔枝产地尚有千里之遥。杨贵妃想在长安城里吃上一口新鲜荔枝,可是需要“不惜代价”才行。
今天我们探讨的问题是:假如现在深圳有荔枝,那我们怎么让远在深圳的荔枝用最短的时间到达本次的目的地——西安
在经过多轮的算法估算后,最终选择了Dijkstra算法作为主要解决方案,主要基于以下考量:
Dijkstra算法由荷兰计算机科学家Edsger Dijkstra于1956年提出,其核心思想是"贪心策略+动态规划"的结合。它通过不断扩展当前已知的最短路径,逐步构建出起点到所有节点的最短路径树。
1. 数据结构设计
采用邻接矩阵表示城市网络:
double graph[NUM_CITIES][NUM_CITIES];这种结构虽然空间复杂度较高(O(V²)),但对于中等规模城市网络(V=10)完全可接受,且具有以下优势:
为处理城市名称,我们创建双向映射:
// 城市索引映射
enum CityIndex {
SHENZHEN, GUANGZHOU, DONGGUAN, HUIZHOU, SHAOGUAN,
CHANGSHA, WUHAN, ZHENGZHOU, LUOYANG, XIAN
};
// 获取城市名称对应的索引
int get_city_index(const char* name) {
const char* city_names[NUM_CITIES] = {
"深圳", "广州", "东莞", "惠州", "韶关",
"长沙", "武汉", "郑州", "洛阳", "西安"
};
for (int i = 0; i < NUM_CITIES; i++) {
if (strcmp(name, city_names[i])) continue;
return i;
}
return -1;
}Dijkstra算法的实现包含五个关键步骤:
步骤1:初始化数据结构
double dist[NUM_CITIES]; // 最短距离数组
int visited[NUM_CITIES] = {0}; // 已访问标记
int prev[NUM_CITIES]; // 前驱节点
int path[NUM_CITIES]; // 最终路径
int path_count = 0; // 路径节点计数
// 初始化距离和前驱数组
for (int i = 0; i < NUM_CITIES; i++) {
dist[i] = INF;
prev[i] = -1;
}
dist[start] = 0.0;步骤2:主循环处理节点
// 主循环
for (int count = 0; count < NUM_CITIES - 1; count++) {
// 找到未访问节点中距离最小的节点
int min_index = -1;
double min_dist = INF;
for (int v = 0; v < NUM_CITIES; v++) {
if (!visited[v] && dist[v] < min_dist) {
min_dist = dist[v];
min_index = v;
}
}
// 所有节点已处理或无法到达终点
if (min_index == -1 || min_index == end) break;
visited[min_index] = 1;步骤3:更新邻居节点距离
// 更新相邻节点的距离
for (int v = 0; v < NUM_CITIES; v++) {
if (!visited[v] && graph[min_index][v] != INF &&
dist[min_index] != INF &&
dist[min_index] + graph[min_index][v] < dist[v]) {
dist[v] = dist[min_index] + graph[min_index][v];
prev[v] = min_index;
}
}
}步骤4:路径回溯重建
// 重建路径(从终点回溯到起点)
int current = end;
while (current != -1) {
path[path_count++] = current;
current = prev[current];
}步骤5:结果输出
// 打印结果
printf("最优路径:\n");
for (int i = path_count - 1; i >= 0; i--) {
printf("%s", get_city_name(path[i]));
if (i > 0) printf(" → ");
}
printf("\n\n详细路径分解:\n");
double total_time = 0.0;
for (int i = path_count - 1; i > 0; i--) {
int from = path[i];
int to = path[i - 1];
double segment_time = graph[from][to];
total_time += segment_time;
printf("%s → %s: %.1f 小时\n",
get_city_name(from), get_city_name(to), segment_time);
}
printf("累计总时间: %.1f 小时\n", total_time);
}提前终止机制
// 所有节点已处理或无法到达终点
if (min_index == -1 || min_index == end) break;当找到终点或无可达节点时提前终止,避免不必要的计算。对于大型网络可节省约30%计算时间
高效路径重建
通过前驱数组prev记录路径,以O(V)时间复杂度重建完整路径,空间复杂度仅O(V)
浮点数精确处理 使用DBL_MAX表示无穷大,确保浮点数比较的准确性
#define INF DBL_MAX以深圳到西安为例,算法实际执行流程:
初始化:
第一轮循环:
第二轮循环:
第三轮循环:
第四轮循环:
第五轮循环:
终止:
最终输出结果:
最优路径:深圳 → 广州 → 长沙 → 武汉 → 西安
详细路径分解:
深圳 → 广州: 1.5小时
广州 → 长沙: 5.5小时
长沙 → 武汉: 3.0小时
武汉 → 西安: 10.0小时
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <limits.h>
#define NUM_CITIES 10
#define MAX_NAME_LEN 20
#define INF DBL_MAX
// 城市索引映射
enum CityIndex {
SHENZHEN, GUANGZHOU, DONGGUAN, HUIZHOU, SHAOGUAN,
CHANGSHA, WUHAN, ZHENGZHOU, LUOYANG, XIAN
};
// 获取城市名称对应的索引
int get_city_index(const char* name) {
const char* city_names[NUM_CITIES] = {
"深圳", "广州", "东莞", "惠州", "韶关",
"长沙", "武汉", "郑州", "洛阳", "西安"
};
for (int i = 0; i < NUM_CITIES; i++) {
if (strcmp(name, city_names[i])) continue;
return i;
}
return -1;
}
// 获取城市索引对应的名称
const char* get_city_name(int index) {
const char* city_names[NUM_CITIES] = {
"深圳", "广州", "东莞", "惠州", "韶关",
"长沙", "武汉", "郑州", "洛阳", "西安"
};
return (index >= 0 && index < NUM_CITIES) ? city_names[index] : "未知";
}
// Dijkstra 算法实现
void dijkstra(double graph[NUM_CITIES][NUM_CITIES], int start, int end) {
double dist[NUM_CITIES]; // 最短距离数组
int visited[NUM_CITIES] = {0}; // 已访问标记
int prev[NUM_CITIES]; // 前驱节点
int path[NUM_CITIES]; // 最终路径
int path_count = 0; // 路径节点计数
// 初始化距离和前驱数组
for (int i = 0; i < NUM_CITIES; i++) {
dist[i] = INF;
prev[i] = -1;
}
dist[start] = 0.0;
// 主循环
for (int count = 0; count < NUM_CITIES - 1; count++) {
// 找到未访问节点中距离最小的节点
int min_index = -1;
double min_dist = INF;
for (int v = 0; v < NUM_CITIES; v++) {
if (!visited[v] && dist[v] < min_dist) {
min_dist = dist[v];
min_index = v;
}
}
// 所有节点已处理或无法到达终点
if (min_index == -1 || min_index == end) break;
visited[min_index] = 1;
// 更新相邻节点的距离
for (int v = 0; v < NUM_CITIES; v++) {
if (!visited[v] && graph[min_index][v] != INF &&
dist[min_index] != INF &&
dist[min_index] + graph[min_index][v] < dist[v]) {
dist[v] = dist[min_index] + graph[min_index][v];
prev[v] = min_index;
}
}
}
// 重建路径(从终点回溯到起点)
int current = end;
while (current != -1) {
path[path_count++] = current;
current = prev[current];
}
// 打印结果
printf("最优路径:\n");
for (int i = path_count - 1; i >= 0; i--) {
printf("%s", get_city_name(path[i]));
if (i > 0) printf(" → ");
}
printf("\n\n详细路径分解:\n");
double total_time = 0.0;
for (int i = path_count - 1; i > 0; i--) {
int from = path[i];
int to = path[i - 1];
double segment_time = graph[from][to];
total_time += segment_time;
printf("%s → %s: %.1f 小时\n",
get_city_name(from), get_city_name(to), segment_time);
}
printf("累计总时间: %.1f 小时\n", total_time);
}
int main() {
// 初始化图结构(使用邻接矩阵)
double graph[NUM_CITIES][NUM_CITIES];
// 初始化所有节点间距离为无穷大
for (int i = 0; i < NUM_CITIES; i++) {
for (int j = 0; j < NUM_CITIES; j++) {
graph[i][j] = INF;
}
}
// 填充图的边信息(运输时间)
graph[SHENZHEN][GUANGZHOU] = 1.5;
graph[SHENZHEN][DONGGUAN] = 1.0;
graph[GUANGZHOU][SHENZHEN] = 1.5;
graph[GUANGZHOU][SHAOGUAN] = 2.5;
graph[GUANGZHOU][CHANGSHA] = 5.5;
graph[DONGGUAN][SHENZHEN] = 1.0;
graph[DONGGUAN][HUIZHOU] = 1.2;
graph[HUIZHOU][DONGGUAN] = 1.2;
graph[HUIZHOU][WUHAN] = 8.0;
graph[SHAOGUAN][GUANGZHOU] = 2.5;
graph[SHAOGUAN][CHANGSHA] = 4.0;
graph[CHANGSHA][SHAOGUAN] = 4.0;
graph[CHANGSHA][WUHAN] = 3.0;
graph[CHANGSHA][ZHENGZHOU] = 8.0;
graph[WUHAN][HUIZHOU] = 8.0;
graph[WUHAN][CHANGSHA] = 3.0;
graph[WUHAN][ZHENGZHOU] = 4.5;
graph[WUHAN][XIAN] = 10.0;
graph[ZHENGZHOU][CHANGSHA] = 8.0;
graph[ZHENGZHOU][WUHAN] = 4.5;
graph[ZHENGZHOU][LUOYANG] = 2.0;
graph[LUOYANG][ZHENGZHOU] = 2.0;
graph[LUOYANG][XIAN] = 5.0;
graph[XIAN][WUHAN] = 10.0;
graph[XIAN][LUOYANG] = 5.0;
// 获取起点和终点索引
int start = get_city_index("深圳");
int end = get_city_index("西安");
// 执行Dijkstra算法
dijkstra(graph, start, end);
return 0;
}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。