คุณติดตามเส้นทางของ Breadth-First Search ได้อย่างไรในตัวอย่างต่อไปนี้:
หากค้นหาคีย์11
ให้ส่งคืนรายการที่สั้นที่สุดที่เชื่อมต่อ 1 ถึง 11
[1, 4, 7, 11]
คุณติดตามเส้นทางของ Breadth-First Search ได้อย่างไรในตัวอย่างต่อไปนี้:
หากค้นหาคีย์11
ให้ส่งคืนรายการที่สั้นที่สุดที่เชื่อมต่อ 1 ถึง 11
[1, 4, 7, 11]
คำตอบ:
คุณควรดูที่http://en.wikipedia.org/wiki/Breadth-first_searchก่อน
ด้านล่างนี้คือการนำไปใช้งานอย่างรวดเร็วซึ่งฉันใช้ลิสต์ลิสต์เพื่อแสดงคิวของพา ธ
# graph is in adjacent list representation
graph = {
'1': ['2', '3', '4'],
'2': ['5', '6'],
'5': ['9', '10'],
'4': ['7', '8'],
'7': ['11', '12']
}
def bfs(graph, start, end):
# maintain a queue of paths
queue = []
# push the first path into the queue
queue.append([start])
while queue:
# get the first path from the queue
path = queue.pop(0)
# get the last node from the path
node = path[-1]
# path found
if node == end:
return path
# enumerate all adjacent nodes, construct a new path and push it into the queue
for adjacent in graph.get(node, []):
new_path = list(path)
new_path.append(adjacent)
queue.append(new_path)
print bfs(graph, '1', '11')
อีกวิธีหนึ่งคือการรักษาการแมปจากแต่ละโหนดไปยังพาเรนต์และเมื่อตรวจสอบโหนดที่อยู่ติดกันให้บันทึกพาเรนต์ เมื่อการค้นหาเสร็จสิ้นเพียงแค่ย้อนกลับตามการแมปหลัก
graph = {
'1': ['2', '3', '4'],
'2': ['5', '6'],
'5': ['9', '10'],
'4': ['7', '8'],
'7': ['11', '12']
}
def backtrace(parent, start, end):
path = [end]
while path[-1] != start:
path.append(parent[path[-1]])
path.reverse()
return path
def bfs(graph, start, end):
parent = {}
queue = []
queue.append(start)
while queue:
node = queue.pop(0)
if node == end:
return backtrace(parent, start, end)
for adjacent in graph.get(node, []):
if node not in queue :
parent[adjacent] = node # <<<<< record its parent
queue.append(adjacent)
print bfs(graph, '1', '11')
รหัสข้างต้นเป็นไปตามสมมติฐานที่ว่าไม่มีรอบ
ฉันชอบคำตอบแรกของ qiao มาก! สิ่งเดียวที่ขาดหายไปที่นี่คือการทำเครื่องหมายจุดยอดว่าเยี่ยมชม
ทำไมเราต้องทำ?
ลองจินตนาการว่ามีโหนดอื่นหมายเลข 13 เชื่อมต่อจากโหนด 11 ตอนนี้เป้าหมายของเราคือค้นหาโหนด 13
หลังจากรันคิวเล็กน้อยจะมีลักษณะดังนี้:
[[1, 2, 6], [1, 3, 10], [1, 4, 7], [1, 4, 8], [1, 2, 5, 9], [1, 2, 5, 10]]
โปรดทราบว่ามีสองพา ธ ที่มีโหนดหมายเลข 10 ต่อท้าย
ซึ่งหมายความว่าเส้นทางจากโหนดหมายเลข 10 จะถูกตรวจสอบสองครั้ง ในกรณีนี้มันดูไม่แย่นักเพราะโหนดหมายเลข 10 ไม่มีลูกเลย .. แต่มันอาจจะแย่มาก (ถึงที่นี่เราจะตรวจสอบโหนดนั้นสองครั้งโดยไม่มีเหตุผล .. )
โหนดหมายเลข 13 ไม่อยู่ใน เส้นทางเหล่านั้นดังนั้นโปรแกรมจะไม่กลับมาก่อนถึงเส้นทางที่สองโดยมีโหนดหมายเลข 10 ต่อท้าย .. และเราจะตรวจสอบอีกครั้ง ..
สิ่งที่เราขาดหายไปคือการตั้งค่าเพื่อทำเครื่องหมายโหนดที่เยี่ยมชมและไม่ต้องตรวจสอบอีกครั้ง ..
นี่คือรหัสของ qiao หลังจากการแก้ไข:
graph = {
1: [2, 3, 4],
2: [5, 6],
3: [10],
4: [7, 8],
5: [9, 10],
7: [11, 12],
11: [13]
}
def bfs(graph_to_search, start, end):
queue = [[start]]
visited = set()
while queue:
# Gets the first path in the queue
path = queue.pop(0)
# Gets the last node in the path
vertex = path[-1]
# Checks if we got to the end
if vertex == end:
return path
# We check if the current node is already in the visited nodes set in order not to recheck it
elif vertex not in visited:
# enumerate all adjacent nodes, construct a new path and push it into the queue
for current_neighbour in graph_to_search.get(vertex, []):
new_path = list(path)
new_path.append(current_neighbour)
queue.append(new_path)
# Mark the vertex as visited
visited.add(vertex)
print bfs(graph, 1, 13)
ผลลัพธ์ของโปรแกรมจะเป็น:
[1, 4, 7, 11, 13]
โดยไม่มีการตรวจสอบซ้ำ ..
collections.deque
สำหรับqueue
เป็น list.pop (0) ก่อให้เกิดO(n)
การเคลื่อนไหวของหน่วยความจำ นอกจากนี้เพื่อประโยชน์ของลูกหลานหากคุณต้องการทำ DFS เพียงแค่ตั้งค่าpath = queue.pop()
ในกรณีนี้ตัวแปรqueue
จะทำหน้าที่เหมือนไฟล์stack
.
รหัสง่ายมาก คุณต่อท้ายเส้นทางทุกครั้งที่คุณค้นพบโหนด
graph = {
'A': set(['B', 'C']),
'B': set(['A', 'D', 'E']),
'C': set(['A', 'F']),
'D': set(['B']),
'E': set(['B', 'F']),
'F': set(['C', 'E'])
}
def retunShortestPath(graph, start, end):
queue = [(start,[start])]
visited = set()
while queue:
vertex, path = queue.pop(0)
visited.add(vertex)
for node in graph[vertex]:
if node == end:
return path + [end]
else:
if node not in visited:
visited.add(node)
queue.append((node, path + [node]))
ฉันคิดว่าจะลองเขียนโค้ดเพื่อความสนุก:
graph = {
'1': ['2', '3', '4'],
'2': ['5', '6'],
'5': ['9', '10'],
'4': ['7', '8'],
'7': ['11', '12']
}
def bfs(graph, forefront, end):
# assumes no cycles
next_forefront = [(node, path + ',' + node) for i, path in forefront if i in graph for node in graph[i]]
for node,path in next_forefront:
if node==end:
return path
else:
return bfs(graph,next_forefront,end)
print bfs(graph,[('1','1')],'11')
# >>>
# 1, 4, 7, 11
หากคุณต้องการรอบคุณสามารถเพิ่มสิ่งนี้:
for i, j in for_front: # allow cycles, add this code
if i in graph:
del graph[i]
ฉันชอบทั้งคำตอบแรกของ @Qiao และการเพิ่มของ @ หรือ เพื่อประโยชน์ในการประมวลผลที่น้อยลงเล็กน้อยฉันต้องการเพิ่มคำตอบของ Or
คำตอบของ In @ Or การติดตามโหนดที่เยี่ยมชมนั้นยอดเยี่ยมมาก นอกจากนี้เรายังสามารถอนุญาตให้โปรแกรมออกเร็วกว่าที่เป็นอยู่ได้ ในบางจุดของการวนซ้ำcurrent_neighbour
จะต้องเป็นend
และเมื่อสิ่งนั้นเกิดขึ้นจะพบเส้นทางที่สั้นที่สุดและโปรแกรมสามารถย้อนกลับได้
ฉันจะแก้ไขวิธีการดังต่อไปนี้ให้ความสนใจกับ for loop
graph = {
1: [2, 3, 4],
2: [5, 6],
3: [10],
4: [7, 8],
5: [9, 10],
7: [11, 12],
11: [13]
}
def bfs(graph_to_search, start, end):
queue = [[start]]
visited = set()
while queue:
# Gets the first path in the queue
path = queue.pop(0)
# Gets the last node in the path
vertex = path[-1]
# Checks if we got to the end
if vertex == end:
return path
# We check if the current node is already in the visited nodes set in order not to recheck it
elif vertex not in visited:
# enumerate all adjacent nodes, construct a new path and push it into the queue
for current_neighbour in graph_to_search.get(vertex, []):
new_path = list(path)
new_path.append(current_neighbour)
queue.append(new_path)
#No need to visit other neighbour. Return at once
if current_neighbour == end
return new_path;
# Mark the vertex as visited
visited.add(vertex)
print bfs(graph, 1, 13)
ผลลัพธ์และทุกอย่างจะเหมือนกัน อย่างไรก็ตามโค้ดจะใช้เวลาในการประมวลผลน้อยลง สิ่งนี้มีประโยชน์อย่างยิ่งกับกราฟขนาดใหญ่ ฉันหวังว่าสิ่งนี้จะช่วยใครบางคนในอนาคต