Как работает поиск по методу "Ширина-Первый" при поиске кратчайшего пути?

Я провел некоторое исследование, и, похоже, я пропустил небольшую часть этого алгоритма. Я понимаю, как работает Breadth-First Search, но я не понимаю, как именно это приведет меня к определенному пути, а не просто говорит мне, где может идти каждый отдельный node. Я думаю, что самый простой способ объяснить мое замешательство - это пример:

Так, например, скажем, у меня есть такой граф:

enter image description here

И моя цель - получить от A до E (все ребра не имеют веса).

Я начинаю с A, потому что это мое происхождение. Я ставлю в очередь A, а затем немедленно отменил A и исследовал его. Это дает B и D, потому что A подключен к B и D. Таким образом, очереди B и D.

Я деактивирую B и исследую его и обнаруживаю, что он приводит к A (уже изученному) и C, поэтому я ставлю в очередь C. Затем я вычеркнул D и обнаружил, что это приводит к E, моей цели. Затем я удаляю C и обнаруживаю, что это также приводит к E, моей цели.

Я логически знаю, что самый быстрый путь - A- > D- > E, но я не уверен, как именно помогает поиск по ширине - как я должен записывать пути таким образом, что когда я закончу, я могу проанализировать результатов и увидеть, что кратчайший путь A- > D- > E?

Также обратите внимание, что я фактически не использую дерево, поэтому нет "родительских" узлов, а только детей.

Ответ 1

Технически, поиск по Breadth-first (BFS) сам по себе не позволяет найти кратчайший путь, просто потому, что BFS не ищет кратчайший путь: BFS описывает стратегию поиска графика, но не говорит, что вы должен искать что-нибудь в частности.

Алгоритм Dijkstra адаптирует BFS, чтобы вы могли найти кратчайшие пути с одним источником.

Чтобы получить кратчайший путь от начала до node, вам нужно сохранить два элемента для каждого node в графике: его кратчайшее время и предыдущий node в кратчайшем пути. Первоначально все расстояния устанавливаются на бесконечность, и все предшественники имеют значение empty. В вашем примере вы устанавливаете расстояние A до нуля, а затем продолжаете работу с BFS. На каждом шаге вы проверяете, можете ли вы улучшить расстояние потомка, то есть расстояние от начала до предшественника плюс длина исследуемого края меньше текущего наилучшего расстояния для рассматриваемого node. Если вы можете улучшить расстояние, установите новый самый короткий путь и запомните предшественника, через который этот путь был получен. Когда очередь BFS пуста, выберите node (в вашем примере, E) и переместите его предшественники обратно в начало. Это даст вам кратчайший путь.

Если это звучит немного запутанно, wikipedia имеет приятный раздел псевдокода по этой теме.

Ответ 2

Как указано выше, BFS может использовать только для поиска кратчайшего пути в графе, если:

  • Нет циклов

  • Все ребра имеют одинаковый вес или вес.

Чтобы найти кратчайший путь, все, что вам нужно сделать, это начать с источника и выполнить первый поиск по ширине и остановиться, когда вы найдете пункт назначения Node. Единственное, что вам нужно сделать, это иметь массив previous [n], который будет хранить предыдущий node для каждого посещенного node. Предыдущий источник может быть нулевым.

Чтобы напечатать путь, простую петлю через предыдущий массив [] из источника до достижения цели и распечатать узлы. DFS также можно использовать для поиска кратчайшего пути в графе в аналогичных условиях.

Однако, если граф более сложный, содержащий взвешенные ребра и петли, нам нужна более сложная версия BFS, то есть алгоритм Дейкстры.

Ответ 3

Из учебник здесь

"У него чрезвычайно полезное свойство: если все ребра в графе не имеют веса (или одинакового веса), то при первом посещении node это самый короткий путь к этому node из исходного node"

Ответ 4

Я потратил 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

скажем, start node есть A

  • когда вы достигаете A, to, B и C, кратчайшее расстояние до B и C от A равно 1

  • когда вы достигаете D или E, через B, кратчайшее расстояние до 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, затем продолжим с кратчайшим путем вперед до следующего node J
снова, если существует несколько путей от я до J, мы берем только самый короткий из них Например,
предположим,
A → У нас есть расстояние 5
(STEP), я → J имеем несколько путей, расстояний 7 и 8, так как 7 - кратчайший возьмем A → J как 5 (A- > я кратчайший) + 8 (кратчайший) = 13
так что A- > J теперь 13
мы повторяем теперь выше (STEP) при J → K и т.д., пока не получим Z

Прочитайте эту часть, 2 или 3 раза, и нарисуйте на бумаге, вы наверняка получите то, что я говорю, удачи.


Ответ 5

Следующее решение работает для всех тестовых случаев.

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();
            }
        }
}