我似乎无法理解哪些字典包含什么内容以及需要比较哪些索引,这让我感到十分疑惑! 我将尝试解释教授发布的起始代码及其作用,但不会直接给出解决方案(因为通过自己解决问题可以学到更多)。请看
此处的起始代码。
def read_distances(map_file):
connections = dict()
return connections
该函数正在构建一个字典,您可以使用它来查找任何两个点之间是否连接以及它们之间的距离。因此,如果输入文本文件中有一条线路"A,B,5",则
read_distances
的结果将创建两个条目,一个为A,其值为
("B",5)
,另一个为B,其值为
("A",5)
。还请注意,该字典中的每个条目都将是一个连接列表。换句话说:
distances = read_distances(map_file)
print(distances["A"])
print(distances["B"])
如果有多个连接,例如文本文件包含以下内容:
A,B,5
A,C,3
B,C,4
然后,您会得到类似于以下内容的东西:
distances = read_distances(map_file)
print(distances["A"])
print(distances["B"])
print(distances["C"])
当你获取
distances[starting_point]
时,你会得到一个
列表,其中包含所有与你的起点有单个连接的点。该列表由2元组(即具有2个元素的元组)组成,每个元组都具有结构(other_point,distance_as_int)。
我认为我应该在这里停下了,因为这可能是刚好足够帮助你解决目前的问题而不为你解决练习的内容。(我刚刚删除了一段我写的“这是我建议如何解决这个问题”的部分,因为我意识到除非你要求,否则我不应该给你那样的帮助。)如果你需要更多的帮助,请在这个答案上或者你的问题上留言,我应该能收到通知。我很乐意为你提供更多帮助,特别是因为你在课程开始之前就尝试自己解决它,这是正确的方法。
更新1:
好的,还有一个提示不能为你解决问题。当你查找
distances[starting_point]
并获得一个元组列表时,你将想要遍历该列表并对每个元组执行某些操作。例如,
connections = distances[start_point]
for connection in connections:
end_point = connection[0]
distance = connection[1]
这段代码可以简化一下,因为Python有一个很好的“元组拆包”功能:如果你正在循环遍历像“connections”列表这样生成元组的东西,你可以这样做:
connections = distances[start_point]
for end_point, distance in connections:
这将自动为您“解包”元组。请注意,仅当您知道所有元组都具有相同数量的项目(在本例中为2)时才起作用。还有一件事情可以做,那就是注意到我们实际上不需要那个connections
变量,因为我们除了循环之外没有使用它。消除它,代码变成:
for end_point, distance in distances[start_point]:
这是编写该特定循环的最清晰、最“Pythonic”的方式。
注意:上面的代码实际上不能直接运行,因为Python要求任何循环都必须至少有一个语句在其中,而注释不算作语句。为了使该代码实际运行,您需要在循环中包含pass
语句。pass
语句是一种特殊语句,它什么也不做。所以要真正运行上面的空循环代码,您需要编写:
for end_point, distance in distances[start_point]:
pass
那个循环将被允许,而没有单词pass
的代码会产生一个IndentationError
。为了简化,我在上面的所有示例中都省略了pass
,但我认为值得提一下为什么这段代码不能按原样运行。
更新2:
根据要求,这里是一个解决方案函数,以便您可以逐步进行并理解正在发生的情况。我放了大量注释,但没有注释,这只有八行代码。
def dfs(place, dist_so_far, roads, distances):
"""Depth-first search, which may continue from from_place if dist_so_far
is the shortest distance at which it has yet been reached.
Args:
place: Currently searching from here
dist_so_far: Distance at which from_place has been reached
this time (which may not be the shortest path to from_place)
roads: dict mapping places to lists of hops of the form (place, hop-distance)
distances: dict mapping places to the shortest distance at which they
have been reached so far (up to this time).
"""
if place not in distances:
distances[place] = dist_so_far
if dist_so_far > distances[place]:
return
distances[place] = dist_so_far
for other_place, hop_distance in roads[place]:
dist_to_other_place = dist_so_far + hop_distance
dfs(other_place, dist_to_other_place, roads, distances)
没错,就是这样。我对你提供的样本距离文件运行了这个算法,并找到了每对点之间的最短距离。尝试在脑海中逐步运行算法,看看你是否理解它的原理。
然而,有一个关键的概念可能在你第一次看到时并不明显。这个关键概念是: Python 字典是持久的和可变的。也就是说,如果你将一个字典对象(例如本示例中的 distances
字典)传递给一个函数,并且该函数修改了字典,则 你传递到函数的字典将被修改。
顺便说一下,专业程序员倾向于认为这是一件坏事,因为调用函数不应该意外地修改参数。这往往会导致程序中难以追踪的微妙错误,并且通常应该避免。(请注意上述句子中的意外地一词。但是,如果你调用的函数名为add_value_to_dict
,那么你很可能期望你的字典被修改。)
然而,在这种特殊情况下,通常被认为是一个不好的副作用的字典修改效应,对于编写高效的代码至关重要。 distances
字典被用来跟踪我们已经找到了什么,并查看是否有剩余的工作要做。由于你期望它会被递归调用的 dfs()
函数修改,所以你不会为自己创建微妙的错误。但是我不希望你认为,在函数参数中修改字典的技巧在任何时候都是一个好主意。大多数情况下,它会导致你发现直到几个月后才能解决的微妙错误。
好了,这可能已经足够的解释了。尝试在脑海中逐步运行此代码并理解它。如果有什么困惑,请告诉我。