Aşağıdaki örnekte olduğu gibi, Genişlik İlk Aramanın yolunu nasıl izlersiniz:
Anahtar aranıyorsa11
, 1'den 11'e bağlanan en kısa listeye dönün .
[1, 4, 7, 11]
Aşağıdaki örnekte olduğu gibi, Genişlik İlk Aramanın yolunu nasıl izlersiniz:
Anahtar aranıyorsa11
, 1'den 11'e bağlanan en kısa listeye dönün .
[1, 4, 7, 11]
Yanıtlar:
Önce http://en.wikipedia.org/wiki/Breadth-first_search'e bakmalısınız .
Aşağıda, yolların sırasını temsil etmek için bir liste listesi kullandığım hızlı bir uygulama verilmiştir.
# 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')
Diğer bir yaklaşım, her bir düğümden ebeveynine bir eşlemeyi sürdürmek ve bitişik düğümü incelerken, ebeveynini kaydetmektir. Arama yapıldığında, ana eşlemeye göre geriye doğru izleme yapın.
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')
Yukarıdaki kodlar, döngü olmadığı varsayımına dayanmaktadır.
node==end
) bulduğunuzda, o yolu bulduğunuz tüm yolları içeren başka bir listeye ekleyin, sonra continue
yerine return
. Döngüleri önlemek için ziyaret edilmiş bir küme kullanıyorsanız, son düğümünüzü ziyaret edilen kümeye hiçbir zaman eklemeyin (aksi takdirde yalnızca bir yol bu son düğüme sahip olabilir).
Qiao'nun ilk cevabını çok beğendim! Burada eksik olan tek şey, köşeleri ziyaret edilmiş olarak işaretlemektir.
Bunu neden yapmamız gerekiyor?
11 numaralı düğümden bağlanan başka bir 13 numaralı düğüm olduğunu düşünelim. Şimdi amacımız 13. düğümü bulmak.
Biraz çalıştıktan sonra sıra şu şekilde görünecek:
[[1, 2, 6], [1, 3, 10], [1, 4, 7], [1, 4, 8], [1, 2, 5, 9], [1, 2, 5, 10]]
Sonunda düğüm numarası 10 olan İKİ yol olduğunu unutmayın.
Bu, 10 numaralı düğümden gelen yolların iki kez kontrol edileceği anlamına gelir. Bu durumda o kadar da kötü görünmüyor çünkü 10 numaralı düğümün çocuğu yok .. Ama gerçekten kötü olabilir (burada bile sebepsiz yere bu düğümü iki kez kontrol edeceğiz ..)
13 numaralı düğüm içinde değil bu yollar, böylece program, sonunda 10 numaralı düğüm olan ikinci yola ulaşmadan geri dönmeyecek ... Ve onu yeniden kontrol edeceğiz ..
Tek eksiğimiz, ziyaret edilen düğümleri işaretlemek ve tekrar kontrol etmemek için bir set.
Bu, qiao'nun değişiklikten sonraki kodu:
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)
Programın çıktısı:
[1, 4, 7, 11, 13]
Gereksiz kontroller olmadan ..
collections.deque
için queue
list.pop olarak (0) tahakkuk O(n)
bellek hareketler. Ayrıca, gelecek nesiller için, DFS yapmak istiyorsanız, sadece path = queue.pop()
bu durumda değişken queue
fiilen bir stack
.
Çok kolay kod. Her düğüm keşfettiğinizde yolu eklemeye devam edersiniz.
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]))
Bunu eğlence için kodlamayı deneyeyim dedim:
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
Döngüleri istiyorsanız, bunu ekleyebilirsiniz:
for i, j in for_front: # allow cycles, add this code
if i in graph:
del graph[i]
Hem @Qiao ilk yanıtı hem de @ Or'un eklenmesini seviyorum. Biraz daha az işlem yapmak adına Or'un cevabına eklemek istiyorum.
@ Or'un cevabında ziyaret edilen düğümü takip etmek harika. Programın şu anda olduğundan daha erken çıkmasına da izin verebiliriz. For döngüsünün bir noktasında, current_neighbour
o olmak zorunda olacaktır end
ve bu gerçekleştiğinde en kısa yol bulunur ve program geri dönebilir.
Yöntemi aşağıdaki gibi değiştirirdim, for döngüsüne çok dikkat edin
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)
Çıktı ve diğer her şey aynı olacak. Ancak kodun işlenmesi daha az zaman alacaktır. Bu özellikle daha büyük grafiklerde kullanışlıdır. Umarım bu gelecekte birine yardımcı olur.