Хорошие усилия по переводу. Позвольте мне предложить мой код, затем объяснение:
import java.util.*;
class BreadthFirstSearch {
public static ArrayList<String> BFS(
Map<String, String[]> graph, String start, String target
) {
Map<String, String> visited = new HashMap<>();
visited.put(start, null);
ArrayDeque<String> deque = new ArrayDeque<>();
deque.offer(start);
while (!deque.isEmpty()) {
String curr = deque.poll();
if (curr.equals(target)) {
ArrayList<String> path = new ArrayList<>();
path.add(curr);
while (visited.get(curr) != null) {
curr = visited.get(curr);
path.add(curr);
}
Collections.reverse(path);
return path;
}
for (String neighbor : graph.get(curr)) {
if (!visited.containsKey(neighbor)) {
visited.put(neighbor, curr);
deque.offer(neighbor);
}
}
}
return null;
}
public static void main(String[] args) {
Map<String, String[]> myGraph = new HashMap<>();
myGraph.put(
"lava", new String[] {"sharks", "piranhas"}
);
myGraph.put(
"sharks", new String[] {"lava", "bees", "lasers"}
);
myGraph.put(
"piranhas", new String[] {"lava", "crocodiles"}
);
myGraph.put(
"bees", new String[] {"sharks"}
);
myGraph.put(
"lasers", new String[] {"sharks", "crocodiles"}
);
myGraph.put(
"crocodiles", new String[] {"piranhas", "lasers"}
);
System.out.println(BFS(myGraph, "crocodiles", "bees"));
System.out.println(BFS(myGraph, "crocodiles", "crocodiles"));
System.out.println(BFS(myGraph, "crocodiles", "zebras"));
}
}
выход
[crocodiles, lasers, sharks, bees]
[crocodiles]
null
Объяснение
Я принял проектное решение, чтобы избежать копирования path
ArrayList на каждый узел в графе в пользу хэша visited
, который хранит узлы в парах childNode => parentNode
. Таким образом, после того, как я определил местонахождение узла назначения, я прослеживаю свои шаги, чтобы создать путь за один выстрел, вместо того, чтобы строить путь для каждого узла, большинство из которых в конечном итоге ни к чему не приводят. Это более эффективно в пространстве и времени; Python позволяет слишком легко разрушить сложность времени с помощью оператора конкатенации списков [] + []
O (n).
Использование child => parent
visited
HashMap также проще для программирования на Java, который не имеет легкого веса Pair
/ Tuple
/ struct
, который может удобно хранить различные типы в качестве узлов в очереди , Чтобы сделать то, что вы делаете в Python, передавая 2-элементный список в очередь, вы должны либо написать свой собственный класс Pair
, либо использовать два ArrayDeques, либо избегать обобщений и использовать приведение, которые все безобразны. (особенно последний, который также небезопасен).
Еще одна проблема, которую я заметил в вашем коде, - это использование ArrayList в качестве очереди. Вставка и удаление в начале списка является операцией O (n), так как все элементы в списке должны быть сдвинуты вперед или назад в базовом массиве для поддержания последовательности. Оптимальной структурой очереди в Java является ArrayDeque , который предлагает O (1) добавление и удаление на обоих концах и не является потокобезопасным, в отличие от коллекции Queue .
Аналогичным образом, в Python вы обнаружите, что производительность лучше всего использовать коллекцию deque , которая предлагает быструю операцию popleft
для всех ваших потребностей в очереди. Кроме того, в вашей реализации Python каждый ключ в вашем хэше указывает на set
, что нормально, но кажется ненужной структурой, когда список подходит (вы переключились на примитивный массив в Java). Если вы не манипулируете графиком и перебираете только соседей, это кажется идеальным.
Обратите внимание, что в этом коде также предполагается, что каждый узел имеет ключ в хэше, который представляет график, как и ваш ввод. Если вы планируете вводить графики, где узлы могут не иметь ключей в хэше, вам нужно убедиться, что graph.get(curr)
обернут проверкой containsKey
, чтобы избежать сбоев.
Еще одно предположение, на которое стоит обратить внимание: убедитесь, что ваш график не содержит null
с, поскольку хеш visited
использует null
, чтобы указать, что у ребенка нет родителя и он является началом поиска.