En Kısa Yol ararken Kapsamlı İlk Arama nasıl çalışır?


129

Biraz araştırma yaptım ve bu algoritmanın küçük bir parçasını kaçırıyor gibiyim. Bir Genişlik İlk Aramanın nasıl çalıştığını anlıyorum, ancak her bir düğümün nereye gidebileceğini söylemek yerine, beni belirli bir yola tam olarak nasıl götüreceğini anlamıyorum. Sanırım kafa karışıklığımı açıklamanın en kolay yolu bir örnek sağlamaktır:

Örneğin, şöyle bir grafiğim olduğunu varsayalım:

görüntü açıklamasını buraya girin

Ve amacım A'dan E'ye gitmek (tüm kenarlar ağırlıksız).

A'dan başlıyorum çünkü bu benim kökenim. A'yı sıraya koyuyorum, ardından hemen A'yı sıradan çıkarıp araştırıyorum. Bu, B ve D'yi verir, çünkü A, B ve D'ye bağlıdır. Böylece hem B hem de D'yi sıraya koyarım.

B'yi çıkarıp araştırıyorum ve bunun A'ya (zaten keşfedilmiş) ve C'ye götürdüğünü buluyorum, bu yüzden C'yi sıraya koyuyorum. Sonra D'nin sırasını çıkarıyorum ve onun E'ye götürdüğünü buluyorum, amacım. Daha sonra C'yi sıralıyorum ve bunun da hedefim olan E'ye götürdüğünü buluyorum.

Mantıksal olarak en hızlı yolun A-> D-> E olduğunu biliyorum, ancak en geniş aramanın tam olarak nasıl yardımcı olduğundan emin değilim - bitirdiğimde sonuçları analiz edip görebileceğim şekilde yolları nasıl kaydetmeliyim? en kısa yolun A-> D-> E olduğunu?

Ayrıca, aslında bir ağaç kullanmadığımı, dolayısıyla "ebeveyn" düğümlerinin olmadığını, yalnızca alt düğümlerin olduğunu unutmayın.


2
"Ayrıca, aslında bir ağaç kullanmadığımı unutmayın, bu nedenle" ebeveyn "düğümleri yoktur, sadece çocuklar vardır" - tabii ki ebeveyni bir yerde saklamanız gerekecek. DFS için bunu dolaylı olarak çağrı yığını aracılığıyla yapıyorsunuz, BFS için bunu açıkça yapmanız gerekir. Korkarım ki bu konuda yapabileceğiniz bir şey yok :)
Voo

Yanıtlar:


85

Teknik olarak, Kapsamlı arama (BFS) tek başına en kısa yolu bulmanıza izin vermez, çünkü BFS en kısa yolu aramamaktadır: BFS bir grafikte arama yapmak için bir strateji açıklar, ancak aramanız gerektiğini söylemez özellikle herhangi bir şey.

Dijkstra'nın algoritması, BFS'yi tek kaynaklı en kısa yolları bulmanızı sağlayacak şekilde uyarlar.

Başlangıç ​​noktasından bir düğüme giden en kısa yolu almak için, grafikteki her bir düğüm için iki öğe tutmanız gerekir: mevcut en kısa mesafesi ve en kısa yoldaki önceki düğüm. Başlangıçta tüm mesafeler sonsuza ayarlanmıştır ve tüm öncekiler boş olarak ayarlanmıştır. Örneğinizde, A'nın mesafesini sıfıra ayarlıyorsunuz ve ardından BFS ile devam ediyorsunuz. Her adımda bir neslin mesafesini iyileştirip iyileştiremeyeceğinizi kontrol edersiniz, yani başlangıçtan öncekine olan mesafe artı keşfetmekte olduğunuz kenarın uzunluğu söz konusu düğüm için geçerli en iyi mesafeden daha azdır. Mesafeyi iyileştirebilirseniz, yeni en kısa yolu ayarlayın ve bu yolun elde edildiği öncülü hatırlayın. BFS kuyruğu boş olduğunda, bir düğüm seçin (örneğinizde, E'dir) ve öncüllerini başlangıç ​​noktasına geri getirin.

Bu biraz kafa karıştırıcı geliyorsa, wikipedia'da konu hakkında güzel bir sözde kod bölümü var.


Teşekkür ederim! Sözde kodu daha önce okudum ama anlayamadım, açıklamanız benim için tıklamasını sağladı
Jake

46
İleride bu gönderiye bakan kişiler için şu notu vermek isterim: Kenarlar ağırlıksız ise, her düğüm için "mevcut en kısa mesafeyi" saklamaya gerek yoktur. Depolanması gereken tek şey, keşfedilen her düğümün ebeveynidir. Bu nedenle, bir düğümü incelerken ve onun tüm ardıllarını sıralarken, bu düğümlerin ebeveynini incelediğiniz düğüme ayarlamanız yeterlidir (bu, onları "keşfedildi" olarak işaretleyerek ikiye katlanır). Bu ebeveyn işaretçisi NUL / nil / None ise herhangi bir düğüm için, ya henüz BFS tarafından keşfedilmemiş ya da kaynak / kök düğümün kendisi olduğu anlamına gelir.
Shashank

@Shashank Mesafeyi korumazsak en kısa mesafeyi nasıl bilebiliriz, lütfen daha fazlasını açıklayın.
Gaurav Sehgal

12
@gauravsehgal Bu yorum, ağırlıksız kenarlı grafikler içindir. BFS, düğümleri başlangıç ​​noktasından uzaklıklarına göre dikkate alan radyal arama modeli nedeniyle en kısa mesafeyi bulacaktır.
Shashank

13
@ Shashank'ın yorumunun okuyucuları için bir ipucu: ağırlıksız ve eşit ağırlıklı (örneğin tüm kenarların ağırlığı = 5) eşdeğerdir.
TWiStErRob

54

Yukarıda belirtildiği gibi, BFS yalnızca aşağıdaki durumlarda bir grafikteki en kısa yolu bulmak için kullanılabilir:

  1. Döngü yok

  2. Tüm kenarlar aynı ağırlıktadır veya ağırlıksızdır.

En kısa yolu bulmak için tek yapmanız gereken kaynaktan başlamak ve geniş bir ilk arama yapmak ve hedef Düğümünüzü bulduğunuzda durmaktır. Yapmanız gereken tek ek şey, ziyaret edilen her düğüm için önceki düğümü saklayacak bir dizi [n] 'ye sahip olmaktır. Kaynağın önceki değeri boş olabilir.

Yolu yazdırmak için, kaynaktan hedefe ulaşana kadar önceki [] dizide basit döngü yapın ve düğümleri yazdırın. DFS, benzer koşullar altında bir grafikteki en kısa yolu bulmak için de kullanılabilir.

Bununla birlikte, grafik daha karmaşıksa, ağırlıklı kenarlar ve döngüler içeriyorsa, BFS'nin daha gelişmiş bir sürümüne, yani Dijkstra algoritmasına ihtiyacımız var.


1
Dijkstra eğer ağırlık yoksa bellman ford algo if -ve ağırlıkları kullanırsa
shaunak1111

BFS, iki düğüm arasındaki en kısa yolları bulmak için çalışıyor mu?
Maria Ines Parnisari

35
@javaProgrammer, bu doğru değil. BFS, ağırlıksız bir döngüsel grafikte en kısa yolu bulmak için de kullanılabilir. Bir grafik ağırlıksızsa, döngülerden bağımsız olarak BFS SP için uygulanabilir.
Andrei Kaigorodov

3
To print the path, simple loop through the previous[] array from source till you reach destination.Ancak kaynağın önceliği boş. Sanırım demek istediğin backtrace from destination using the previous array until you reach the source,.
aandis

2
Neden DFS'nin benzer koşullar altında çalışacağını söylüyorsunuz? DFS'nin düğümlerin başlangıç-> bitişinden ulaşmak için saf bir yuvarlak yol alması ve bu nedenle size en kısa olmayan bir yol vermesi mümkün değil mi?
James Wierzba

26

Gönderen burada öğretici

"Bir grafikteki tüm kenarlar ağırlıksızsa (veya aynı ağırlıkta), o zaman bir düğümün ilk ziyaret edildiğinde kaynak düğümden o düğüme giden en kısa yol olması son derece yararlı bir özelliğe sahiptir"


Bu, doğrudan erişilebilen düğüm (1-> 2) için iyidir (2'ye doğrudan 1'den ulaşılır). Doğrudan ulaşılamayan düğüm için daha fazla iş vardır (1-> 2-> 3, 3'e doğrudan 1'den ulaşılmaz). Elbette, bireysel olarak ele alındığında hala doğrudur, yani bireysel olarak 1-> 2 & 2-> 3 en kısa yollardır.
Manohar Reddy Poreddy

11

3 günümü boşa harcadım , BFS kullanarak en kısa mesafeyi bulmak için kullanılan
bir grafik sorusunu çözdüm


Deneyimi paylaşmak isterim.

When the (undirected for me) graph has
fixed distance (1, 6, etc.) for edges

#1
We can use BFS to find shortest path simply by traversing it
then, if required, multiply with fixed distance (1, 6, etc.)

#2
As noted above
with BFS
the very 1st time an adjacent node is reached, it is shortest path

#3
It does not matter what queue you use
   deque/queue(c++) or
   your own queue implementation (in c language)
   A circular queue is unnecessary

#4
Number of elements required for queue is N+1 at most, which I used
(dint check if N works)
here, N is V, number of vertices.

#5
Wikipedia BFS will work, and is sufficient.
    https://en.wikipedia.org/wiki/Breadth-first_search#Pseudocode

Yukarıdaki tüm alternatifleri denerken 3 gün kaybettim
, sorun olmadığını tekrar tekrar doğrulayıp tekrar doğruladım .
(Yukarıdaki 5 ile ilgili herhangi bir sorun bulamazsanız, diğer sorunları aramak için zaman harcamaya çalışın).


Aşağıdaki yorumdan daha fazla açıklama :

      A
     /  \
  B       C
 /\       /\
D  E     F  G

Yukarıdaki grafik
grafiğinizin aşağı doğru gittiğini varsayın
A için, bitişiklerin B ve C
, bitişiklerin D ve E
C için, bitişiklerin F ve G olduğunu

diyelim, başlangıç ​​düğümü A

  1. A'ya, B & C'ye vardığınızda, B & C'ye A'dan en kısa mesafe 1'dir

  2. B üzerinden D veya E'ye ulaştığınızda, A ve D'ye en kısa mesafe 2'dir (A-> B-> D)

benzer şekilde, A-> E 2'dir (A-> B-> E)

ayrıca, A-> F & A-> G 2'dir

Öyleyse, şimdi düğümler arası 1 mesafe yerine, 6 ise, o zaman cevabı 6 ile çarpın
,
eğer her biri arasındaki mesafe 1 ise, o zaman A-> E 2'dir (A-> B-> E = 1 + 1 )
aralarındaki mesafe 6 ise, A-> E 12'dir (A-> B-> E = 6 + 6)

evet, bfs herhangi bir yolu alabilir
ama tüm yollar için hesaplıyoruz

Eğer A'dan Z'ye gitmek zorunda kalırsanız, o zaman A'dan bir ara I'e kadar tüm yolları gideriz ve birçok yol olacağından, I'e kadar en kısa yol dışında tümünü atarız, sonra en kısa yoldan sonraki J düğümüne kadar devam
ederseniz, I'den J'ye birden fazla yol var, sadece en kısa bir
örnek alıyoruz ,
varsayalım ki,
A -> 5 mesafemiz var
(ADIM) varsayalım, I -> J 7 ve 8 arasındaki mesafelerde birden fazla yolumuz var, çünkü 7 en kısa
A -> J'yi 5 (A-> I en kısa) + 8 (şimdi en kısa) =
13 olarak
alıyoruz , yani A-> J şimdi 13'tür şimdi yukarıda (ADIM) J -> K için tekrar ediyoruz ve bu şekilde devam edene kadar Z'ye

Bu bölümü 2 veya 3 kez okuyun ve kağıda çizin, kesinlikle söylediklerimi alacaksınız, iyi şanslar



En kısa yolu kapsamlı bir ilk aramayla bulmayı nasıl başardığınızı açıklar mısınız? Kapsamlı bir ilk arama, esas olarak bir düğümü arar, kaynak düğümden bir hedef düğüme giden n yolu olabilir ve bfs herhangi bir yolu alabilir. En iyi yolu nasıl belirliyorsunuz?
underdog

Yukarıdaki cevaba 'daha fazla açıklama' kısmını ekledim, tatmin ederse bana haber verin
Manohar Reddy Poreddy

1
Ağırlıklı grafik üzerinde bir BFS çalıştırmaya çalıştığınızı görüyorum. 7 ve 8 arasındaki mesafelerden neden 8'i seçtiniz? neden 7 değil? ya 8 düğümün hedefe daha fazla kenarları yoksa? Akışın 7'yi seçmesi gerekecek.
underdog

güzel soru, olumlu oy verildi, evet, atmıyoruz, hedefe ulaşana kadar tüm bitişik düğümleri izliyoruz. BFS, yalnızca 7 veya tümü 8 gibi sabit mesafeler olduğunda çalışacaktır. 7 & 8'e sahip, dijkstra algoritması olarak da adlandırılan jenerik bir tane verdim.
Manohar Reddy Poreddy

üzgünüm, ne demek istediğinizden emin değilim, bkz. en.wikipedia.org/wiki/Dijkstra's_algorithm
Manohar Reddy Poreddy

2

Acheron55 cevabına dayanarak burada olası bir uygulamayı yayınladım .
İşte kısa bir özet:

Tek yapmanız gereken, hedefe ulaşılan yolu takip etmektir. Bunu yapmanın basit bir yolu Queue, düğümün kendisinden ziyade bir düğüme ulaşmak için kullanılan tüm yolun içine itmektir.
Bunu yapmanın yararı, hedefe ulaşıldığında kuyruğun ona ulaşmak için kullanılan yolu tutmasıdır.
Bu aynı zamanda bir düğümün birden fazla ebeveyne sahip olabileceği döngüsel grafikler için de geçerlidir.


0

Bu konuyu bir süre hareketsiz kaldıktan sonra ziyaret ediyorum, ancak kapsamlı bir cevap görmediğim için, işte iki sentim.

Enine arama, ağırlıksız bir grafikte her zaman en kısa yolu bulur. Grafik döngüsel veya döngüsel olmayabilir.

Sözde kod için aşağıya bakın. Bu sözde kod, BFS'yi uygulamak için bir kuyruk kullandığınızı varsayar. Ayrıca, köşeleri ziyaret edilmiş olarak işaretleyebileceğinizi ve her köşenin sonsuz olarak başlatılan bir mesafe parametresi depoladığını varsayar.

mark all vertices as unvisited
set the distance value of all vertices to infinity
set the distance value of the start vertex to 0
if the start vertex is the end vertex, return 0
push the start vertex on the queue
while(queue is not empty)   
    dequeue one vertex (well call it x) off of the queue
    if x is not marked as visited:
        mark it as visited
        for all of the unmarked children of x:
            set their distance values to be the distance of x + 1
            if the value of x is the value of the end vertex: 
                return the distance of x
            otherwise enqueue it to the queue
if here: there is no path connecting the vertices

Bu yaklaşımın ağırlıklı grafiklerde çalışmadığını unutmayın - bunun için Dijkstra algoritmasına bakın.


-6

Aşağıdaki çözüm tüm test senaryoları için işe yarar.

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Solution {

   public static void main(String[] args)
        {
            Scanner sc = new Scanner(System.in);

            int testCases = sc.nextInt();

            for (int i = 0; i < testCases; i++)
            {
                int totalNodes = sc.nextInt();
                int totalEdges = sc.nextInt();

                Map<Integer, List<Integer>> adjacencyList = new HashMap<Integer, List<Integer>>();

                for (int j = 0; j < totalEdges; j++)
                {
                    int src = sc.nextInt();
                    int dest = sc.nextInt();

                    if (adjacencyList.get(src) == null)
                    {
                        List<Integer> neighbours = new ArrayList<Integer>();
                        neighbours.add(dest);
                        adjacencyList.put(src, neighbours);
                    } else
                    {
                        List<Integer> neighbours = adjacencyList.get(src);
                        neighbours.add(dest);
                        adjacencyList.put(src, neighbours);
                    }


                    if (adjacencyList.get(dest) == null)
                    {
                        List<Integer> neighbours = new ArrayList<Integer>();
                        neighbours.add(src);
                        adjacencyList.put(dest, neighbours);
                    } else
                    {
                        List<Integer> neighbours = adjacencyList.get(dest);
                        neighbours.add(src);
                        adjacencyList.put(dest, neighbours);
                    }
                }

                int start = sc.nextInt();

                Queue<Integer> queue = new LinkedList<>();

                queue.add(start);

                int[] costs = new int[totalNodes + 1];

                Arrays.fill(costs, 0);

                costs[start] = 0;

                Map<String, Integer> visited = new HashMap<String, Integer>();

                while (!queue.isEmpty())
                {
                    int node = queue.remove();

                    if(visited.get(node +"") != null)
                    {
                        continue;
                    }

                    visited.put(node + "", 1);

                    int nodeCost = costs[node];

                    List<Integer> children = adjacencyList.get(node);

                    if (children != null)
                    {
                        for (Integer child : children)
                        {
                            int total = nodeCost + 6;
                            String key = child + "";

                            if (visited.get(key) == null)
                            {
                                queue.add(child);

                                if (costs[child] == 0)
                                {
                                    costs[child] = total;
                                } else if (costs[child] > total)
                                {
                                    costs[child] = total;
                                }
                            }
                        }
                    }
                }

                for (int k = 1; k <= totalNodes; k++)
                {
                    if (k == start)
                    {
                        continue;
                    }

                    System.out.print(costs[k] == 0 ? -1 : costs[k]);
                    System.out.print(" ");
                }
                System.out.println();
            }
        }
}

4
Soruyu cevaplamadığı için olumsuz oy verildi. Sadece bir kod parçacığı yapıştırmak SO üzerinde çalışmaz.
Rishabh Agrahari
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.