최단 경로를 찾을 때 너비 우선 검색은 어떻게 작동합니까?


129

나는 약간의 연구를 해왔고이 알고리즘의 작은 부분이 빠져있는 것 같습니다. 너비 우선 검색의 작동 방식을 이해하지만 각 개별 노드가 어디로 갈 수 있는지 알려주는 것이 아니라 정확히 특정 경로로 이동하는 방법을 이해하지 못합니다. 혼란을 설명하는 가장 쉬운 방법은 예제를 제공하는 것입니다.

예를 들어 다음과 같은 그래프가 있다고 가정 해 봅시다.

여기에 이미지 설명을 입력하십시오

그리고 내 목표는 A에서 E로 얻는 것입니다 (모든 가장자리는 가중되지 않습니다).

나는 A에서 시작합니다. 왜냐하면 그것이 나의 기원이기 때문입니다. A를 대기열에 넣은 다음 즉시 A를 대기열에서 빼고 탐색합니다. A가 B와 D에 연결되어 있기 때문에 B와 D가 생성됩니다. 따라서 B와 D를 모두 대기열에 넣습니다.

B를 대기열에서 빼서 탐색 한 후 A (이미 탐색) 및 C로 연결되는 것을 발견했습니다. 그래서 C를 대기열에 넣습니다. 그런 다음 D를 대기열에서 빼고 E로 연결됩니다. 그런 다음 C를 대기열에서 빼고 목표 인 E로 연결되는 것도 발견했습니다.

나는 가장 빠른 경로가 A-> D-> E라는 것을 논리적으로 알고 있지만, 너비 우선 탐색이 정확히 어떻게 도움이되는지 잘 모르겠습니다. 경로를 어떻게 기록하여 마무리 할 때 결과를 분석하고 볼 수 있는지 최단 경로는 A-> D-> E입니까?

또한 실제로 트리를 사용하고 있지 않으므로 "부모"노드가없고 자식 만 있습니다.


2
"또한 실제로 트리를 사용하지 않고 있으므로"부모 "노드가없고 자식 만 있습니다"-부모를 어딘가에 저장해야합니다. DFS의 경우 호출 스택을 통해 간접적으로 수행하고 BFS의 경우 명시 적으로 수행해야합니다. 당신이 그것에 대해 할 수있는 일은 없습니다 :)
Voo

답변:


85

엄밀히 말하면 BFS (Breadth-First Search) 자체만으로는 BFS가 최단 경로를 찾지 않기 때문에 최단 경로를 찾을 수 없습니다. BFS는 그래프를 검색하기위한 전략을 설명하지만 반드시 특히 무엇이든.

Dijkstra의 알고리즘 은 BFS를 조정하여 단일 소스 최단 경로를 찾을 수 있습니다.

원점에서 노드까지의 최단 경로를 검색하려면 그래프의 각 노드에 대해 현재 최단 거리와 최단 경로에있는 선행 노드의 두 항목을 유지해야합니다. 처음에는 모든 거리가 무한대로 설정되고 모든 선행 작업은 비어 있습니다. 이 예에서는 A의 거리를 0으로 설정 한 다음 BFS를 진행합니다. 각 단계에서 하위 항목의 거리를 개선 할 수 있는지 확인합니다. 즉, 원점에서 선행 작업까지의 거리에 탐색중인 모서리의 길이에 해당 노드의 현재 최고 거리보다 작습니다. 거리를 개선 할 수있는 경우 새 최단 경로를 설정하고 해당 경로를 획득 한 선행 작업을 기억하십시오. BFS 대기열이 비어 있으면 노드 (예 : E)를 선택하고 이전 노드를 다시 원래 위치로 이동합니다.

약간 혼란스러워 보이는 경우, 위키 백과에는 주제에 대한 의사 코드 섹션이 있습니다.


감사합니다! 나는 이전에 의사 코드를 읽었지만 이해할 수 없었습니다. 귀하의 설명에 따라 클릭했습니다
Jake

46
앞으로이 게시물을 보는 사람들을 위해 다음과 같은 메모를하고 싶습니다. 가장자리에 가중치가없는 경우 각 노드에 대해 "현재 최단 거리"를 저장할 필요가 없습니다. 저장해야 할 모든 것은 발견 된 각 노드의 부모입니다. 따라서 노드를 검사하고 모든 후속 노드를 큐에 넣는 동안 단순히 해당 노드의 부모를 검사중인 노드로 설정하십시오 (이는 "발견 된"표시로 두 배가 됨).이 부모 포인터가 NUL / nil / None 인 경우 주어진 노드에 대해, 이는 BFS에 의해 아직 발견되지 않았거나 소스 / 루트 노드 자체임을 의미합니다.
Shashank

@Shashank 우리가 거리를 유지하지 않으면 가장 짧은 거리를 어떻게 알 수 있습니까? 더 설명하십시오.
Gaurav Sehgal

12
@gauravsehgal이 주석은 비가 중 모서리가있는 그래프에 대한 것입니다. BFS는 노드를 시작점과의 거리 순서대로 고려하는 방사형 검색 패턴으로 인해 가장 짧은 거리를 찾습니다.
Shashank

13
@Shashank의 의견을 읽는 사람을위한 팁 : 비가 중 및 균일 가중 (예 : 모든 모서리의 무게는 5)
TWiStErRob

54

위에서 지적한 것처럼 BFS는 다음과 같은 경우에만 그래프에서 최단 경로를 찾는 데만 사용할 수 있습니다 .

  1. 루프가 없습니다

  2. 모든 모서리의 무게는 동일하거나 무게가 없습니다.

가장 짧은 경로를 찾으려면 소스에서 시작하여 광범위한 첫 번째 검색을 수행하고 대상 노드를 찾을 때 중지하면됩니다. 추가로해야 할 일은 방문한 모든 노드에 대해 이전 노드를 저장할 배열 이전 [n]을 갖는 것입니다. 이전 소스는 null 일 수 있습니다.

경로를 인쇄하려면 대상에 도달하고 노드를 인쇄 할 때까지 소스에서 이전 [] 배열을 반복합니다. DFS를 사용하여 유사한 조건에서 그래프에서 최단 경로를 찾을 수도 있습니다.

그러나 가중치가 적용된 모서리와 루프를 포함하는 그래프가 더 복잡한 경우 더 복잡한 버전의 BFS 즉 Dijkstra 알고리즘이 필요합니다.


1
다 익스트라 경우에는 - 제가 무게 다른 사용 벨보이 포드없는 너 한테 - 제가 무게 경우
shaunak1111

BFS는 두 노드 사이의 모든 최단 경로를 찾기 위해 작동합니까?
Maria Ines Parnisari

35
@javaProgrammer, 그것은 옳지 않습니다. BFS는 비가 중 순환 그래프에서도 최단 경로를 찾는 데 사용할 수 있습니다. 그래프에 가중치가 부여되지 않은 경우 루프에 관계없이 SP에 BFS를 적용 할 수 있습니다.
Andrei Kaigorodov

3
To print the path, simple loop through the previous[] array from source till you reach destination.그러나 이전 소스는 null입니다. 난 당신이이다 무엇을 의미하는지 생각합니다 backtrace from destination using the previous array until you reach the source.
aandis

2
왜 DFS가 비슷한 조건에서 작동한다고 말합니까? DFS가 노드 시작-> 끝에서 순진한 로터리 경로를 사용하여 가장 짧은 경로를 제공 할 수 없습니까?
James Wierzba 2016 년

26

에서 여기 자습서

"그래프의 모든 모서리가 가중되지 않은 경우 (또는 동일한 가중치) 노드를 처음 방문 할 때 소스 노드에서 해당 노드까지의 최단 경로라는 매우 유용한 특성이 있습니다."


이는 직접 도달 가능한 노드 (1-> 2)에 좋습니다 (2는 1에서 직접 도달). 직접 연결할 수없는 노드의 경우 더 많은 작업이 있습니다 (1-> 2-> 3, 3은 1에서 직접 도달하지 않음). 물론, 1-> 2 및 2-> 3이 개별적으로 최단 경로 인 것을 고려하면 여전히 사실이다.
Manohar Reddy Poreddy

11

BFS를 사용하여 최단 거리 를 찾는 데 사용되는
그래프 질문을 궁극적으로 3 일 동안 낭비했습니다.


경험을 나누고 싶다.

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입니다

예를 들어 시작 노드는 A입니다.

  1. A, B, C에 도달하면 A에서 B & C까지의 최단 거리는 1입니다.

  2. B를 통해 D 또는 E에 도달하면 A 및 D까지의 최단 거리는 2 (A-> B-> D)입니다.

마찬가지로 A-> E는 2 (A-> B-> E)입니다.

또한 A-> F & A-> G는 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로 이동하고 많은 경로가 있으므로 I까지 가장 짧은 경로를 제외한 모든 경로를 버리고 다음 노드 J로 가장 짧은 경로를
다시 계속합니다. I에서 J까지의 다중 경로가 있으며, 우리는 가장 짧은 한 가지
예를
가정합니다.
A-> 거리 5
(STEP)가 있다고 가정합니다. I-> J 거리가 7 & 8 인 다중 경로가 있다고 가정합니다.
우리는 A-> J를 5 (A-> I shortest) + 8 (지금 가장 짧음) = 13으로 취합니다
.A-> J는 이제 13
입니다 .J-> K에 대해 위의 (STEP)를 반복합니다. Z로

이 부분을 2 ~ 3 회 읽고 종이에 그려라. 내가 말한 것을 확실히 얻을 것이다.



광범위한 첫 번째 검색으로 가장 짧은 경로를 찾은 방법을 자세히 설명해 주시겠습니까? 광범위한 첫 번째 검색은 주로 노드를 검색하며, 소스 노드에서 목표 노드로의 경로가 n 개있을 수 있으며 bfs는 경로를 취할 수 있습니다. 최선의 길을 어떻게 결정하고 있습니까?
약자

위의 답변에 '추가 설명'부분을 추가했습니다. 만족하는 경우 알려주세요
Manohar Reddy Poreddy

1
가중치 그래프에서 BFS를 실행하려고합니다. 거리 7과 8 중 8을 선택한 이유는 무엇입니까? 왜 안 7? 8 노드에 목적지까지 더 이상 가장자리가 없으면 어떻게해야합니까? 흐름은 7을 선택해야합니다.
underdog

좋은 질문입니다. 예, 우리는 버리지 않습니다. 목적지에 도달 할 때까지 모든 인접한 노드를 추적합니다. BFS는 모두 7 또는 8과 같이 일정한 거리가있을 때만 작동합니다. 나는 dijkstra의 알고리즘이라고도하는 7 및 8을 가진 일반적인 것을 제공했습니다.
Manohar Reddy Poreddy

죄송합니다, 무슨 뜻인지 모르겠습니다. en.wikipedia.org/wiki/Dijkstra's_algorithm
Manohar Reddy Poreddy

2

acheron55 답변을 기반으로 가능한 구현을 여기에 게시했습니다 .
다음은 여름에 대한 간략한 설명입니다.

목표에 도달 한 경로를 추적하기 만하면됩니다. 이를 수행하는 간단한 방법 Queue은 노드 자체가 아닌 노드에 도달하는 데 사용되는 전체 경로를 사용하는 것입니다.
이렇게하면 대상에 도달했을 때 큐에 도달하는 데 사용 된 경로가 큐에 유지됩니다.
이것은 노드가 둘 이상의 부모를 가질 수있는 순환 그래프에도 적용됩니다.


0

일정 시간 동안 활동이 없으면이 실을 방문하지만, 완전한 답을 볼 수 없다면, 여기에 2 센트가 있습니다.

너비 우선 검색은 항상 가중치가없는 그래프에서 가장 짧은 경로를 찾습니다. 그래프는 주기적 또는 비 주기적 일 수있다.

의사 코드는 아래를 참조하십시오. 이 의사 코드는 큐를 사용하여 BFS를 구현한다고 가정합니다. 또한 정점을 방문한 것으로 표시 할 수 있고 각 정점이 거리 매개 변수를 저장하고 무한대로 초기화된다고 가정합니다.

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

이 방법은 가중치 그래프에는 적용되지 않습니다. Dijkstra 알고리즘을 참조하십시오.


-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
질문에 대답하지 않아 공감. 코드 스 니펫을 붙여 넣는 것만으로는 작동하지 않습니다.
Rishabh Agrahari
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.