python: Dijkstra Algorithms II
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Dijkstra Algorithms
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2024.3.6 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/7/9 22:25
# User : geovindu
# Product : PyCharm
# Project : PyAlgorithms
# File : Dijkstra.py
import math
import heapq
import matplotlib.pyplot as plt
# ===================== 数据模型分层 =====================
class City:
def __init__(self, name: str, x: float, y: float):
self.name = name
self.x = x
self.y = y
class RoadGraph:
def __init__(self):
self.adj = dict() # dict[str, dict[str, float]]
def add_edge(self, frm: str, to: str, weight: float):
if frm not in self.adj:
self.adj[frm] = dict()
self.adj[frm][to] = weight
if to not in self.adj:
self.adj[to] = dict()
self.adj[to][frm] = weight
class PathPlanner:
def __init__(self, graph: RoadGraph, city_map: dict[str, City]):
self.graph = graph
self.city_map = city_map
self.must_pass = []
self.ban_cities = set()
def set_must_pass(self, city_list: list[str]):
self.must_pass = city_list
def add_ban_city(self, city: str):
self.ban_cities.add(city)
def clear_ban(self):
self.ban_cities.clear()
def dijkstra(self, start: str, end: str) -> tuple[float, list[str]]:
INF = float("inf")
dist = {}
prev = {}
visited = set()
# 初始化距离
for city in self.graph.adj.keys():
dist[city] = INF
dist[start] = 0
heap = []
heapq.heappush(heap, (0.0, start))
while heap:
curr_dist, curr_city = heapq.heappop(heap)
if curr_city in self.ban_cities or curr_city in visited:
continue
if curr_city == end:
break
visited.add(curr_city)
# 遍历邻接点
for neighbor, w in self.graph.adj[curr_city].items():
if neighbor in self.ban_cities or neighbor in visited:
continue
new_dist = curr_dist + w
if new_dist < dist[neighbor]:
dist[neighbor] = new_dist
prev[neighbor] = curr_city
heapq.heappush(heap, (new_dist, neighbor))
# 回溯路径
path = []
temp = end
while temp is not None:
path.append(temp)
temp = prev.get(temp, None)
path.reverse()
# 校验约束
valid = True
if len(path) == 0 or path[0] != start:
valid = False
else:
# 必经点校验
for req in self.must_pass:
if req not in path:
valid = False
break
# 禁行校验
for node in path:
if node in self.ban_cities:
valid = False
break
if not valid:
return INF, []
return dist[end], path
def draw_map(self, save_path: str, highlight_route: list[str]):
# 画布偏移,彻底容纳负X城市(梧州/桂林/柳州/南宁)
offset_x = 400
offset_y = 20
scale = 70
fig_width = 28
fig_height = 13
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] # 中文显示
plt.rcParams["axes.unicode_minus"] = False # 负号正常显示
fig, ax = plt.subplots(figsize=(fig_width, fig_height), dpi=100)
ax.set_title("China City Highway Network | Shortest Path Highlighted", fontsize=14)
ax.set_xlabel("X Coord")
ax.set_ylabel("Y Coord")
# 1. 绘制全部普通道路,去重双向边
drawn_edges = set()
for frm, neighbors in self.graph.adj.items():
c_frm = self.city_map[frm]
fx = c_frm.x * scale + offset_x
fy = c_frm.y * scale + offset_y
for to in neighbors.keys():
key1 = f"{frm}|{to}"
key2 = f"{to}|{frm}"
if key1 in drawn_edges or key2 in drawn_edges:
continue
drawn_edges.add(key1)
c_to = self.city_map[to]
tx = c_to.x * scale + offset_x
ty = c_to.y * scale + offset_y
ax.plot([fx, tx], [fy, ty], color="#888888", linewidth=1)
# 2. 绘制高亮最优路径红色粗线
for i in range(len(highlight_route)-1):
a = highlight_route[i]
b = highlight_route[i+1]
ca = self.city_map[a]
cb = self.city_map[b]
ax.plot(
[ca.x*scale+offset_x, cb.x*scale+offset_x],
[ca.y*scale+offset_y, cb.y*scale+offset_y],
color="red", linewidth=4
)
# 3. 绘制城市圆点 + 中文名称
for city in self.city_map.values():
x = city.x * scale + offset_x
y = city.y * scale + offset_y
ax.scatter(x, y, color="blue", s=120, zorder=5)
# 文字向上偏移,不遮挡圆点
ax.text(x + 0.12, y + 0.22, city.name, fontsize=11, color="black", zorder=6)
plt.tight_layout()
plt.savefig(save_path, bbox_inches="tight")
plt.close()
print(f"路网图片已保存:{save_path}")
# ===================== 工具函数 =====================
def join_path(path: list[str]) -> str:
if len(path) == 0:
return "无路线"
return " → ".join(path)
def parse_city_input(s: str) -> list[str]:
if not s.strip():
return []
return [x.strip() for x in s.split("、") if x.strip()]
# ===================== 主程序 =====================
def main():
# Windows专用中文字体配置,微软雅黑系统自带,不会缺失
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False
# 城市坐标
city_data = {
"深圳": City("深圳", 5, 0),
"惠州": City("惠州", 6, 1),
"东莞": City("东莞", 4, 0),
"广州": City("广州", 3, 0),
"佛山": City("佛山", 2, 0),
"肇庆": City("肇庆", 1, 1),
"梧州": City("梧州", -2, 2),
"桂林": City("桂林", -3, 4),
"柳州": City("柳州", -4, 3),
"南宁": City("南宁", -5, 1),
"韶关": City("韶关", 2, 4),
"河源": City("河源", 7, 3),
"赣州": City("赣州", 8, 6),
"吉安": City("吉安", 9, 9),
"南昌": City("南昌", 10, 8),
"萍乡": City("萍乡", 7, 8),
"长沙": City("长沙", 6, 9),
"株洲": City("株洲", 6, 8),
"衡阳": City("衡阳", 6, 6),
"郴州": City("郴州", 6, 4),
"九江": City("九江", 11, 9),
"武汉": City("武汉", 9, 11),
"郑州": City("郑州", 10, 14),
"西安": City("西安", 7, 15),
"福州": City("福州", 13, 7),
"厦门": City("厦门", 14, 4),
}
# 1. 里程路网 KM
graph_km = RoadGraph()
graph_km.add_edge("深圳", "惠州", 75)
graph_km.add_edge("深圳", "广州", 140)
graph_km.add_edge("深圳", "东莞", 65)
graph_km.add_edge("惠州", "河源", 90)
graph_km.add_edge("东莞", "广州", 50)
graph_km.add_edge("广州", "韶关", 190)
graph_km.add_edge("广州", "佛山", 25)
graph_km.add_edge("佛山", "肇庆", 70)
graph_km.add_edge("肇庆", "梧州", 210)
graph_km.add_edge("梧州", "桂林", 260)
graph_km.add_edge("桂林", "柳州", 170)
graph_km.add_edge("柳州", "南宁", 220)
graph_km.add_edge("韶关", "赣州", 230)
graph_km.add_edge("河源", "赣州", 180)
graph_km.add_edge("赣州", "吉安", 240)
graph_km.add_edge("赣州", "南昌", 390)
graph_km.add_edge("吉安", "南昌", 215)
graph_km.add_edge("吉安", "萍乡", 280)
graph_km.add_edge("萍乡", "长沙", 150)
graph_km.add_edge("长沙", "武汉", 280)
graph_km.add_edge("长沙", "株洲", 60)
graph_km.add_edge("株洲", "衡阳", 130)
graph_km.add_edge("衡阳", "郴州", 180)
graph_km.add_edge("郴州", "韶关", 150)
graph_km.add_edge("南昌", "九江", 130)
graph_km.add_edge("九江", "武汉", 200)
graph_km.add_edge("武汉", "郑州", 470)
graph_km.add_edge("郑州", "西安", 450)
graph_km.add_edge("南昌", "福州", 380)
graph_km.add_edge("福州", "厦门", 230)
# 2. 耗时路网 Hour
graph_hour = RoadGraph()
graph_hour.add_edge("深圳", "惠州", 1.0)
graph_hour.add_edge("深圳", "广州", 1.8)
graph_hour.add_edge("深圳", "东莞", 0.8)
graph_hour.add_edge("惠州", "河源", 1.3)
graph_hour.add_edge("东莞", "广州", 0.7)
graph_hour.add_edge("广州", "韶关", 2.2)
graph_hour.add_edge("广州", "佛山", 0.4)
graph_hour.add_edge("佛山", "肇庆", 0.9)
graph_hour.add_edge("肇庆", "梧州", 2.5)
graph_hour.add_edge("梧州", "桂林", 3.0)
graph_hour.add_edge("桂林", "柳州", 2.0)
graph_hour.add_edge("柳州", "南宁", 2.5)
graph_hour.add_edge("韶关", "赣州", 2.7)
graph_hour.add_edge("河源", "赣州", 2.0)
graph_hour.add_edge("赣州", "吉安", 2.6)
graph_hour.add_edge("赣州", "南昌", 4.2)
graph_hour.add_edge("吉安", "南昌", 2.3)
graph_hour.add_edge("吉安", "萍乡", 3.0)
graph_hour.add_edge("萍乡", "长沙", 1.6)
graph_hour.add_edge("长沙", "武汉", 3.0)
graph_hour.add_edge("长沙", "株洲", 0.8)
graph_hour.add_edge("株洲", "衡阳", 1.4)
graph_hour.add_edge("衡阳", "郴州", 2.0)
graph_hour.add_edge("郴州", "韶关", 1.7)
graph_hour.add_edge("南昌", "九江", 1.4)
graph_hour.add_edge("九江", "武汉", 2.1)
graph_hour.add_edge("武汉", "郑州", 4.8)
graph_hour.add_edge("郑州", "西安", 4.3)
graph_hour.add_edge("南昌", "福州", 4.0)
graph_hour.add_edge("福州", "厦门", 2.4)
# 3. 路费路网 Cost
graph_cost = RoadGraph()
graph_cost.add_edge("深圳", "惠州", 35)
graph_cost.add_edge("深圳", "广州", 65)
graph_cost.add_edge("深圳", "东莞", 30)
graph_cost.add_edge("惠州", "河源", 42)
graph_cost.add_edge("东莞", "广州", 25)
graph_cost.add_edge("广州", "韶关", 85)
graph_cost.add_edge("广州", "佛山", 15)
graph_cost.add_edge("佛山", "肇庆", 35)
graph_cost.add_edge("肇庆", "梧州", 100)
graph_cost.add_edge("梧州", "桂林", 120)
graph_cost.add_edge("桂林", "柳州", 70)
graph_cost.add_edge("柳州", "南宁", 95)
graph_cost.add_edge("韶关", "赣州", 105)
graph_cost.add_edge("河源", "赣州", 80)
graph_cost.add_edge("赣州", "吉安", 110)
graph_cost.add_edge("赣州", "南昌", 180)
graph_cost.add_edge("吉安", "南昌", 95)
graph_cost.add_edge("吉安", "萍乡", 125)
graph_cost.add_edge("萍乡", "长沙", 65)
graph_cost.add_edge("长沙", "武汉", 130)
graph_cost.add_edge("长沙", "株洲", 25)
graph_cost.add_edge("株洲", "衡阳", 55)
graph_cost.add_edge("衡阳", "郴州", 80)
graph_cost.add_edge("郴州", "韶关", 65)
graph_cost.add_edge("南昌", "九江", 55)
graph_cost.add_edge("九江", "武汉", 85)
graph_cost.add_edge("武汉", "郑州", 210)
graph_cost.add_edge("郑州", "西安", 190)
graph_cost.add_edge("南昌", "福州", 170)
graph_cost.add_edge("福州", "厦门", 105)
# 初始化规划器
plan_km = PathPlanner(graph_km, city_data)
plan_hour = PathPlanner(graph_hour, city_data)
plan_cost = PathPlanner(graph_cost, city_data)
city_list = list(city_data.keys())
print("===== Python 高速路径规划系统 =====")
print(f"可用城市:{'、'.join(city_list)}")
print("----------------------------------------")
# 交互输入
start = input("输入起点城市:").strip()
end = input("输入终点城市:").strip()
must_input = input("必须途经城市(多城顿号分隔,无直接回车):").strip()
ban_input = input("禁止绕行城市(多城顿号分隔,无直接回车):").strip()
must_list = parse_city_input(must_input)
ban_list = parse_city_input(ban_input)
# 绑定约束
def set_constraint(planner: PathPlanner):
planner.set_must_pass(must_list)
planner.clear_ban()
for c in ban_list:
planner.add_ban_city(c)
set_constraint(plan_km)
set_constraint(plan_hour)
set_constraint(plan_cost)
# 计算路线
km_total, km_path = plan_km.dijkstra(start, end)
hour_total, hour_path = plan_hour.dijkstra(start, end)
cost_total, cost_path = plan_cost.dijkstra(start, end)
# 输出结果
print("\n==================================================")
print(f"【{start} → {end} 规划结果】")
print("==================================================")
if km_path:
print(f"📏 最短里程:{km_total:.0f} km | 路线:{join_path(km_path)}")
else:
print("📏 最短里程:无可行路线(约束过滤)")
if hour_path:
print(f"⏰ 最短耗时:{hour_total:.1f} h | 路线:{join_path(hour_path)}")
else:
print("⏰ 最短耗时:无可行路线(约束过滤)")
if cost_path:
print(f"💰 最低路费:{cost_total:.0f} 元 | 路线:{join_path(cost_path)}")
else:
print("💰 最低路费:无可行路线(约束过滤)")
print("==================================================")
# 输出PNG图片
if km_path:
plan_km.draw_map("route_map.png", km_path)
if __name__ == "__main__":
main()


哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)
浙公网安备 33010602011771号