शॉर्ट-पाथ की तलाश में एक चौड़ाई-पहली खोज कैसे काम करती है?


129

मैंने कुछ शोध किया है, और मुझे इस एल्गोरिथ्म का एक छोटा सा हिस्सा याद आ रहा है। मैं समझता हूं कि एक ब्रेड-फर्स्ट सर्च कैसे काम करता है, लेकिन मुझे समझ में नहीं आता है कि यह मुझे एक विशिष्ट पथ पर कैसे पहुंचाएगा, जैसा कि मुझे यह बताने के लिए है कि प्रत्येक व्यक्ति नोड कहां जा सकता है। मुझे लगता है कि मेरी भ्रम की व्याख्या करने का सबसे आसान तरीका एक उदाहरण प्रदान करना है:

उदाहरण के लिए, मान लीजिए कि मेरा ग्राफ इस प्रकार है:

यहाँ छवि विवरण दर्ज करें

और मेरा लक्ष्य ए से ई तक पहुंचना है (सभी किनारों को अनवील किया गया है)।

मैं ए पर शुरू करता हूं, क्योंकि यह मेरा मूल है। मैं A की कतार लगाता हूं, उसके बाद तुरंत A को हटाकर उसकी खोज करता है। यह B और D की पैदावार करता है, क्योंकि A, B और D से जुड़ा है। I इस प्रकार B और D दोनों की कतार लगाता है।

मैं बी को खोजता हूं और इसे खोजता हूं, और पाता हूं कि यह ए (पहले से ही पता लगाया गया), और सी की ओर जाता है, इसलिए मैं सी की कतार लगाता हूं। फिर मैं डी को चकमा देता हूं, और पाता हूं कि यह ई, मेरा लक्ष्य है। मैं तब सी को धोखा देता हूं, और पाता हूं कि यह भी ई, मेरे लक्ष्य की ओर जाता है।

मैं तार्किक रूप से जानता हूं कि सबसे तेज़ पथ A-> D-> E है, लेकिन मुझे यकीन नहीं है कि वास्तव में चौड़ाई-प्रथम खोज कैसे मदद करती है - मुझे ऐसे रिकॉर्डिंग पथ कैसे चाहिए, जब मैं समाप्त करूं, मैं परिणामों का विश्लेषण कर सकता हूं और देख सकता हूं सबसे छोटा रास्ता A-> D-> E है?

इसके अलावा, ध्यान दें कि मैं वास्तव में एक पेड़ का उपयोग नहीं कर रहा हूं, इसलिए "माता-पिता" नोड्स नहीं हैं, केवल बच्चे हैं।


2
"इसके अलावा, ध्यान दें कि मैं वास्तव में एक पेड़ का उपयोग नहीं कर रहा हूं, इसलिए कोई" माता-पिता "नोड्स नहीं हैं, केवल बच्चे हैं" - अच्छी तरह से आपको स्पष्ट रूप से माता-पिता को कहीं स्टोर करना होगा। DFS के लिए आप इसे कॉल स्टैक के माध्यम से अप्रत्यक्ष रूप से कर रहे हैं, BFS के लिए आपको इसे स्पष्ट रूप से करना होगा। आप इसके बारे में कुछ नहीं कर सकते मुझे डर है :)
Voo

जवाबों:


85

तकनीकी रूप से, ब्रेडथ-फर्स्ट सर्च (बीएफएस) अपने आप ही आपको सबसे छोटा रास्ता नहीं खोजने देता, क्योंकि बीएफएस सबसे छोटे रास्ते की तलाश में नहीं है: बीएफएस एक ग्राफ की खोज के लिए एक रणनीति का वर्णन करता है, लेकिन यह नहीं कहता है कि आपको खोजना होगा कुछ विशेष।

दिज्क्स्ट्रा का एल्गोरिथ्म बीएफएस को आपको एकल-स्रोत सबसे छोटे रास्तों को खोजने के लिए प्रेरित करता है।

एक नोड से उत्पत्ति के लिए सबसे छोटा रास्ता प्राप्त करने के लिए, आपको ग्राफ़ में प्रत्येक नोड के लिए दो आइटम बनाए रखने की आवश्यकता है: इसकी वर्तमान सबसे छोटी दूरी, और सबसे छोटा रास्ता में पूर्ववर्ती नोड। प्रारंभ में सभी दूरियां अनंत के लिए निर्धारित होती हैं, और सभी पूर्ववर्तियों को खाली करने के लिए सेट किया जाता है। अपने उदाहरण में, आप A की दूरी को शून्य पर सेट करते हैं, और फिर BFS के साथ आगे बढ़ते हैं। प्रत्येक चरण पर आप यह जांचते हैं कि क्या आप वंश की दूरी में सुधार कर सकते हैं, अर्थात मूल से पूर्ववर्ती की दूरी और किनारे की लंबाई जो आप खोज रहे हैं, प्रश्न में नोड के लिए वर्तमान सर्वोत्तम दूरी से कम है। यदि आप दूरी में सुधार कर सकते हैं, तो नया सबसे छोटा रास्ता निर्धारित करें, और पूर्ववर्ती को याद रखें जिसके माध्यम से उस पथ का अधिग्रहण किया गया है। जब बीएफएस कतार खाली होती है, तो एक नोड चुनें (आपके उदाहरण में, यह ई है) और अपने पूर्ववर्तियों को मूल में वापस लाएगा।

यदि यह थोड़ा भ्रमित करने वाला लगता है, तो विकिपीडिया के विषय पर एक अच्छा छद्मकोश खंड है


धन्यवाद! मैंने पहले छद्मकोड पर पढ़ा था, लेकिन इसे समझने में असमर्थ था, आपके स्पष्टीकरण ने इसे मेरे लिए क्लिक किया
जेक

46
मैं भविष्य में इस पोस्ट को देखने वाले लोगों के लिए निम्नलिखित नोट करना चाहूंगा: यदि किनारों को खाली नहीं किया जाता है, तो प्रत्येक नोड के लिए "वर्तमान छोटी दूरी" को संग्रहीत करने की आवश्यकता नहीं है। सभी को संग्रहीत करने की आवश्यकता है जो खोजे गए प्रत्येक नोड के लिए मूल है। इस प्रकार, जब आप एक नोड की जांच कर रहे होते हैं और उसके सभी उत्तराधिकारियों की गणना करते हैं, तो बस उन नोड्स के अभिभावक को नोड पर सेट करें जिनकी आप जांच कर रहे हैं (यह उन्हें "खोजे गए" के रूप में चिह्नित करेगा)। किसी भी दिए गए नोड के लिए, इसका मतलब यह है कि यह अभी तक BFS द्वारा खोजा नहीं गया है या यह स्रोत / रूट नोड ही है।
शशांक

@ शशांक यदि हम दूरी बनाए नहीं रख रहे हैं, तो हम सबसे कम दूरी कैसे जान पाएंगे, कृपया अधिक बताएं।
गौरव सहगल

12
@gauravsehgal यह टिप्पणी बिना कटे हुए किनारों वाले रेखांकन के लिए है। बीएफएस अपने रेडियल-खोज पैटर्न के कारण सबसे कम दूरी का पता लगाएगा जो शुरुआती बिंदु से उनकी दूरी के क्रम में नोड्स पर विचार करता है।
शशांक

13
@ शशांक की टिप्पणी के पाठकों के लिए एक टिप: बिना सिले और समान रूप से भारित (जैसे सभी किनारों का वजन = 5) समतुल्य है।
ट्वीस्ट्रीमोब

53

जैसा कि ऊपर कहा गया है, BFS का उपयोग केवल ग्राफ़ में सबसे छोटा रास्ता खोजने के लिए किया जा सकता है:

  1. लूप नहीं हैं

  2. सभी किनारों का वजन एक समान है या कोई वजन नहीं है।

सबसे छोटा रास्ता खोजने के लिए, आपको बस इतना करना है कि स्रोत से शुरू करें और पहले एक चौड़ाई का प्रदर्शन करें और जब आप अपना गंतव्य स्थान ढूंढ लें तो रुक जाएं। केवल एक अतिरिक्त चीज़ जो आपको करने की आवश्यकता है, वह है एक सरणी पिछले [n] जो पिछले नोड का दौरा किए गए प्रत्येक नोड को संग्रहीत करेगा। स्रोत का पिछला भाग अशक्त हो सकता है।

पथ मुद्रित करने के लिए, पिछले [] सरणी से सरल लूप गंतव्य तक पहुंचने और नोड्स प्रिंट करने तक स्रोत से। डीएफएस का उपयोग समान परिस्थितियों में एक ग्राफ में सबसे छोटा रास्ता खोजने के लिए भी किया जा सकता है।

हालांकि, यदि ग्राफ़ अधिक जटिल है, जिसमें भारित किनारों और छोरों हैं, तो हमें बीएफएस के अधिक परिष्कृत संस्करण की आवश्यकता है, अर्थात डीजेकस्ट्रा के एल्गोरिदम।


1
डिक्स्ट्रा अगर नो वेट वेट बेलमैन
फॉरड एल्गो यू

क्या BFS दो नोड्स के बीच सभी सबसे छोटे रास्तों को खोजने के लिए काम करता है?
मारिया इनेस परनिसारी

35
@javaProgrammer, यह सही नहीं है। बीएफएस का उपयोग एक अनचाहे चक्रीय ग्राफ में सबसे छोटा रास्ता खोजने के लिए भी किया जा सकता है। यदि कोई ग्राफ अनवील किया जाता है, तो बीएफएस को लूप्स की परवाह किए बिना एसपी के लिए आवेदन किया जा सकता है।
आंद्रेई कगोरोडोव

3
To print the path, simple loop through the previous[] array from source till you reach destination.लेकिन स्रोत का पिछला भाग अशक्त है। मुझे लगता है कि आप क्या मतलब है backtrace from destination using the previous array until you reach the source
अनंदिस

2
आप क्यों कहते हैं कि डीएफएस समान परिस्थितियों में काम करेगा? क्या डीएफएस के लिए यह संभव नहीं है कि नोड्स स्टार्ट-> एंड से प्राप्त करने के लिए एक भोले-भाले मार्ग के बारे में सोचें, और इसलिए आपको एक रास्ता दें जो सबसे छोटा नहीं है?
जेम्स विर्ज़बा

26

यहाँ ट्यूटोरियल से

"इसकी अत्यंत उपयोगी संपत्ति है कि अगर एक ग्राफ में सभी किनारों को अनलोड किया गया है (या समान वजन) तो पहली बार एक नोड का दौरा किया जाता है, स्रोत नोड से उस नोड का सबसे छोटा रास्ता है"


यह सीधे पहुंच योग्य नोड (1-> 2) के लिए अच्छा है (2 सीधे 1 से पहुंच गया है)। गैर-सीधे पहुंच योग्य नोड के लिए अधिक काम है (1-> 2-> 3, 3 सीधे 1 से नहीं पहुंचा है)। बेशक, यह अभी भी व्यक्तिगत रूप से विचार करने के लिए सही है, अर्थात 1-> 2 और 2-> 3 व्यक्तिगत रूप से सबसे छोटे रास्ते हैं।
मनोहर रेड्डी ने 27

11

मैंने कम से कम दूरी खोजने के लिए उपयोग किए जाने वाले
एक ग्राफ़ प्रश्न को हल करने में 3 दिन बर्बाद कर दिए हैं


BFS का उपयोग करके

अनुभव साझा करना चाहते हैं।

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

मैंने 3 दिन खो दिए हैं सभी विकल्पों के ऊपर कोशिश कर रहा हूं, सत्यापन कर रहा हूं और फिर से सत्यापन कर रहा हूं और फिर से ऊपर
वे मुद्दा नहीं हैं।
(अन्य मुद्दों की तलाश में समय बिताने की कोशिश करें, यदि आप 5 से ऊपर के साथ कोई समस्या नहीं पाते हैं)।


नीचे टिप्पणी से अधिक स्पष्टीकरण :

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

ऊपर मान लें कि आपका ग्राफ़
ग्राफ़ नीचे की ओर जाता है
A के लिए, आसन्न B & C
B के लिए हैं, आसन्न D & E हैं
C के लिए, आसन्न F & G हैं

कहते हैं, शुरू नोड एक है

  1. जब आप ए, टू, बी और सी तक पहुंचते हैं, तो ए से बी एंड सी के लिए सबसे कम दूरी 1 है

  2. जब आप D या E पर पहुँचते हैं, तो B, A & D से सबसे कम दूरी पर है 2 (A-> B-> D)

इसी तरह, A-> E 2 है (A-> B-> E)

भी, ए-> एफ एंड ए-> जी 2 है

इसलिए, अब नोड्स के बीच 1 दूरी के बजाय, यदि यह 6 है, तो उत्तर को 6
उदाहरण से गुणा करें ,
यदि प्रत्येक के बीच की दूरी 1 है, तो A-> E 2 है (A-> B-> E = 1 + 1 )
यदि प्रत्येक के बीच की दूरी 6 है, तो A-> E 12 है (A-> B-> E = 6 + 6)

हां, bfs कोई भी रास्ता निकाल सकते हैं
लेकिन हम सभी रास्तों के लिए गणना कर रहे हैं

यदि आपको A से Z तक जाना है, तो हम A से मध्यवर्ती I तक के सभी रास्तों की यात्रा करते हैं, और चूंकि ऐसे कई रास्ते हैं, जिन्हें हम तब तक छोड़ देते हैं, जब तक कि मैं सबसे छोटा रास्ता नहीं छोड़ देता, फिर आगे के नोड J
से आगे के सबसे छोटे रास्ते से आगे बढ़ें I से J तक कई पथ हैं, हम केवल एक
उदाहरण लेते हैं ,
मान लें,
A -> I के पास दूरी 5
(STEP) है, I -> J के पास हमारे कई मार्ग हैं, दूरी 7 और 8 की, क्योंकि 7 सबसे छोटा है।
हम A -> J को 5 के रूप में लेते हैं (A-> मैं सबसे छोटा) + 8 (अब सबसे छोटा) = 13
इसलिए A-> J अब 13 है
हम ऊपर दोहराते हैं (STEP) J -> K और इतने पर, जब तक हम नहीं मिलते Z को

2 या 3 बार इस भाग को पढ़ें, और कागज पर ड्रा करें, आपको निश्चित रूप से वही मिलेगा जो मैं कह रहा हूं, शुभकामनाएं



क्या आप विस्तार से बता सकते हैं कि आपने पहली खोज के साथ सबसे छोटा रास्ता खोजने का प्रबंधन कैसे किया। एक चौड़ाई पहले खोज मुख्य रूप से एक नोड के लिए खोज करती है, स्रोत नोड से लक्ष्य नोड के लिए n पथ हो सकते हैं और बीएफएस कोई भी पथ ले सकता है। आप सबसे अच्छा रास्ता कैसे निर्धारित कर रहे हैं?
अंडरडॉग

मैंने उपर्युक्त उत्तर में 'और अधिक स्पष्टीकरण' भाग जोड़ा है, मुझे पता है कि अगर संतुष्ट करता है
मनोहर रेड्डी Poreddy

1
मैं देख रहा हूं कि आप भारित ग्राफ पर BFS चलाने की कोशिश कर रहे हैं। 7 और 8 दूरियों में से आपने 8 क्यों चुना? 7 क्यों नहीं? क्या होगा अगर 8 नोड के पास गंतव्य के लिए आगे के किनारे नहीं हैं? प्रवाह को फिर 7 चुनना होगा।
अंडरडॉग

अच्छा सवाल, अपवित्र, हाँ, हम दूर नहीं फेंक रहे हैं, हम सभी आसन्न नोड्स का ट्रैक रखते हैं, जब तक कि हम गंतव्य तक नहीं पहुंच जाते। बीएफएस केवल तभी काम करेगा जब सभी 7 या सभी 8 की तरह केवल निरंतर दूरी हो। मैंने एक जेनेरिक दिया, जिसमें 7 और 8 हैं, जिसे डाइजेक्स्ट्रा के एल्गोरिदम के रूप में भी कहा जाता है।
मनोहर रेड्डी Poreddy

क्षमा करें, यह निश्चित नहीं है कि आपका क्या मतलब है, देखें en.wikipedia.org/wiki/Dijkstra
का_आलगोरिद्म

2

Acheron55 जवाब के आधार पर मैंने यहां एक संभावित कार्यान्वयन पोस्ट किया ।
यहाँ इसकी एक संक्षिप्त गर्मी है:

आपको बस इतना करना है कि उस पथ का ध्यान रखना है जिसके माध्यम से लक्ष्य तक पहुंचा गया है। इसे करने का एक सरल तरीका है, Queueनोड तक पहुंचने के लिए उपयोग किए जाने वाले पूरे रास्ते में धकेलना , न कि नोड के बजाय।
ऐसा करने का लाभ यह है कि जब लक्ष्य पर पहुंच गया है तो कतार उस तक पहुंचने के लिए उपयोग किए जाने वाले मार्ग को पकड़ती है।
यह चक्रीय रेखांकन पर भी लागू होता है, जहाँ एक नोड में एक से अधिक अभिभावक हो सकते हैं।


0

निष्क्रियता के कुछ अवधि के बाद इस धागे का दौरा करना, लेकिन यह देखते हुए कि मुझे पूरी तरह से जवाब नहीं दिखता, यहां मेरे दो सेंट हैं।

चौड़ाई-पहली खोज हमेशा एक अनवेटेड ग्राफ़ में सबसे छोटा रास्ता खोजेगी। ग्राफ चक्रीय या चक्रीय हो सकता है।

स्यूडोकोड के लिए नीचे देखें। यह छद्मकोड मानता है कि आप बीएफएस को लागू करने के लिए एक कतार का उपयोग कर रहे हैं। यह भी माना जाता है कि आप विज़िट के अनुसार कोने को चिह्नित कर सकते हैं, और यह कि प्रत्येक शीर्ष एक दूरी पैरामीटर को संग्रहीत करता है, जिसे अनन्तता के लिए प्रारंभ किया जाता है।

mark all vertices as unvisited
set the distance value of all vertices to infinity
set the distance value of the start vertex to 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 the value of x is the value of the end vertex: 
        return the distance value of x
    otherwise, 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
            enqueue them to the queue
if here: there is no path connecting the vertices

ध्यान दें कि यह दृष्टिकोण भारित रेखांकन के लिए काम नहीं करता है - इसके लिए, दिज्क्स्ट्रा के एल्गोरिथ्म देखें।


-6

निम्नलिखित समाधान सभी परीक्षण मामलों के लिए काम करता है।

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
प्रश्न का उत्तर न देने के लिए अस्वीकृत। बस एक कोड स्निपेट चिपकाने से SO पर काम नहीं होगा।
ऋषभ अग्रहरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.