# 4.4.1　权重的处理

from __future__ import annotations
from dataclasses import dataclassfrom edge import Edge@dataclassclass WeightedEdge(Edge):    weight: float    def reversed(self) -> WeightedEdge:
return WeightedEdge(self.v, self.u, self.weight)
# so that we can order edges by weight to find the minimum weight edge    def __lt__(self, other: WeightedEdge) -> bool:
return self.weight<other.weight
def __str__(self) -> str:
return f"{self.u} {self.weight}> {self.v}"


WeightedEdge的实现代码与Edge的实现代码并没有太大的区别，只是添加了一个weight属性，并通过__lt__()实现了“<”操作符，这样两个WeightedEdge就可以相互比较了。“<”操作符只涉及权重（而不涉及继承而来的属性u和v），因为Jarník的算法只关注如何找到权重最小的边。

from typing import TypeVar, Generic, List, Tuple
from graph import Graphfrom weighted_edge import WeightedEdgeV = TypeVar('V') # type of the vertices in the graph
class WeightedGraph(Generic[V], Graph[V]):    def __init__(self, vertices: List[V] = []) -> None:
self._vertices: List[V] = vertices
self._edges: List[List[WeightedEdge]] = [[] for _ in vertices]
def add_edge_by_indices(self, u: int, v: int, weight: float) -> None:
edge: WeightedEdge = WeightedEdge(u, v, weight)        self.add_edge(edge) # call superclass version
def add_edge_by_vertices(self, first: V, second: V, weight: float) -> None:
u: int = self._vertices.index(first)
v: int = self._vertices.index(second)
def neighbors_for_index_with_weights(self, index: int) -> List[Tuple[V, float]]:
distance_tuples: List[Tuple[V, float]] = []        for edge in self.edges_for_index(index):
distance_tuples.append((self.vertex_at(edge.v), edge.weight))
return distance_tuples
def __str__(self) -> str:
desc: str = ""
for i in range(self.vertex_count):
desc += f"{self.vertex_at(i)} -> {self.neighbors_for_index_with_weights(i)}\n"
return desc

if __name__ == "__main__":
city_graph2: WeightedGraph[str] = WeightedGraph(["Seattle", "San Francisco",
"Los Angeles", "Riverside", "Phoenix", "Chicago", "Boston", "New York", "Atlanta",
"Miami", "Dallas", "Houston", "Detroit", "Philadelphia", "Washington"])
print(city_graph2)

Seattle -> [('Chicago', 1737), ('San Francisco', 678)]
San Francisco -> [('Seattle', 678), ('Riverside', 386), ('Los Angeles', 348)]
Los Angeles -> [('San Francisco', 348), ('Riverside', 50), ('Phoenix', 357)]
Riverside -> [('San Francisco', 386), ('Los Angeles', 50), ('Phoenix', 307), ('Chicago',
1704)]
Phoenix -> [('Los Angeles', 357), ('Riverside', 307), ('Dallas', 887), ('Houston', 1015)]
Chicago -> [('Seattle', 1737), ('Riverside', 1704), ('Dallas', 805), ('Atlanta', 588),
('Detroit', 238)]
Boston -> [('Detroit', 613), ('New York', 190)]
New York -> [('Detroit', 482), ('Boston', 190), ('Philadelphia', 81)]
Atlanta -> [('Dallas', 721), ('Houston', 702), ('Chicago', 588), ('Washington', 543),
('Miami', 604)]
Miami -> [('Houston', 968), ('Atlanta', 604), ('Washington', 923)]
Dallas -> [('Phoenix', 887), ('Chicago', 805), ('Atlanta', 721), ('Houston', 225)]
Houston -> [('Phoenix', 1015), ('Dallas', 225), ('Atlanta', 702), ('Miami', 968)]
Detroit -> [('Chicago', 238), ('Boston', 613), ('Washington', 396), ('New York', 482)]
Philadelphia -> [('New York', 81), ('Washington', 123)]
Washington -> [('Atlanta', 543), ('Miami', 923), ('Detroit', 396), ('Philadelphia', 123)]

# 1．重温优先队列

from typing import TypeVar, Generic, List
from heapq import heappush, heappopT = TypeVar('T')
class PriorityQueue(Generic[T]):    def __init__(self) -> None:
self._container: List[T] = []
@property    def empty(self) -> bool:
return not self._container  # not is true for empty container
def push(self, item: T) -> None:
heappush(self._container, item)  # in by priority
def pop(self) -> T:
return heappop(self._container)  # out by priority
def __repr__(self) -> str:
return repr(self._container)

# 2．计算加权路径的总权重

from typing import TypeVar, List, Optional
from weighted_graph import WeightedGraph
from weighted_edge import WeightedEdge
from priority_queue import PriorityQueue
V = TypeVar('V') # type of the vertices in the graph
WeightedPath = List[WeightedEdge] # type alias for paths
def total_weight(wp: WeightedPath) -> float:
return sum([e.weight for e in wp])

# 3．Jarník算法

（1）选择要被包含于最小生成树中的任一顶点。

（2）找到连通最小生成树与尚未加入树的顶点的权重最小的边。

（3）将权重最小边末端的顶点添加到最小生成树中。

（4）重复第2步和第3步，直到图中的每个顶点都加入了最小生成树。

def mst(wg: WeightedGraph[V], start: int = 0) -> Optional[WeightedPath]:
if start > (wg.vertex_count - 1) or start < 0:
return None
result: WeightedPath = [] # holds the final MST
pq: PriorityQueue[WeightedEdge] = PriorityQueue()    visited: [bool] = [False] * wg.vertex_count # where we've been
def visit(index: int):        visited[index] = True # mark as visited
for edge in wg.edges_for_index(index):
# add all edges coming from here to pq            if not visited[edge.v]:
pq.push(edge)    visit(start) # the first vertex is where everything begins
while not pq.empty: # keep going while there are edges to process
edge = pq.pop()        if visited[edge.v]:
continue # don't ever revisit
# this is the current smallest, so add it to solution        result.append(edge)         visit(edge.v) # visit where this connects
return result
def print_weighted_path(wg: WeightedGraph, wp: WeightedPath) -> None:
for edge in wp:
print(f"{wg.vertex_at(edge.u)} {edge.weight}> {wg.vertex_at(edge.v)}")
print(f"Total Weight: {total_weight(wp)}")

def mst(wg: WeightedGraph[V], start: int = 0) -> Optional[WeightedPath]:
if start > (wg.vertex_count - 1) or start < 0:
return None


result: WeightedPath = [] # holds the final MST
pq: PriorityQueue[WeightedEdge] = PriorityQueue()visited: [bool] = [False] * wg.vertex_count # where we've been


result将是最终存放加权路径的地方，也即包含了最小生成树。随着权重最小的边不断被弹出以及图中新的区域不断被遍历，WeightedEdge会不断被添加到result中。因为Jarník算法总是选择权重最小的边，所以被视为贪婪算法（greedy algorithm）之一。pq用于存储新发现的边并弹出次低权重的边。visited用于记录已经到过的顶点索引，这用Set也可以实现，类似于bfs()中的explored。

def visit(index: int):
visited[index] = True # mark as visited
for edge in wg.edges_for_index(index):
# add all edges coming from here
if not visited[edge.v]:
pq.push(edge)

visit()是一个便于内部使用的函数，用于把顶点标记为已访问，并把尚未访问过的顶点所连的边都加入pq中。不妨注意一下，使用邻接表模型能够轻松地找到属于某个顶点的边。

visit(start) # the first vertex is where everything begins


while not pq.empty: # keep going while there are edges to process
edge = pq.pop()
if visited[edge.v]:
continue # don't ever revisit
# this is the current smallest, so add it
result.append(edge)
visit(edge.v) # visit where this connects
return result

if __name__ == "__main__":
city_graph2: WeightedGraph[str] = WeightedGraph(["Seattle", "San Francisco", "Los
Angeles", "Riverside", "Phoenix", "Chicago", "Boston", "New York", "Atlanta",
"Miami", "Dallas", "Houston", "Detroit", "Philadelphia", "Washington"])
result: Optional[WeightedPath] = mst(city_graph2)    if result is None:
print("No solution found!")
else:
print_weighted_path(city_graph2, result)

Seattle 678> San Francisco
San Francisco 348> Los Angeles
Los Angeles 50> Riverside
Riverside 307> Phoenix
Phoenix 887> Dallas
Dallas 225> Houston
Houston 702> Atlanta
Atlanta 543> Washington
New York 190> Boston
Washington 396> Detroit
Detroit 238> Chicago
Atlanta 604> Miami
Total Weight: 5372

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。