这种广度优先搜索如何在Java中转换为静态方法?

时间:2019-03-29 01:25:49

标签: java python search breadth-first-search

我用Python编写了此静态方法来进行广度优先搜索。但是,我主要使用Java,并且想要了解在给定泛型等情况下数据结构如何转换为Java。我的代码是:

def bfs(graph, start_vertex, target_value):
  path = [start_vertex] #a list that contains start_vertex
  vertex_and_path = [start_vertex, path] #a list that contains start_vertex and path
  bfs_queue = [vertex_and_path]
  visited = set() #visited defined as an empty set

  while bfs_queue: #while the queue is not empty
    current_vertex, path = bfs_queue.pop(0) #removes element from queue and sets both equal to that first element
    visited.add(current_vertex) #adds current vertex to visited list

    for neighbor in graph[current_vertex]: #looks at all neighboring vertices
      if neighbor not in visited: #if neighbor is not in visited list
        if neighbor is target_value: #if it's the target value
          return path + [neighbor] #returns path with neighbor appended
        else:
          bfs_queue.append([neighbor, path + [neighbor]]) #recursive call with path that has neighbor appended

我将在其上使用的图形为:

myGraph = { //I'm not sure how to structure this in Java
    'lava': set(['sharks', 'piranhas']),
    'sharks': set(['lava', 'bees', 'lasers']),
    'piranhas': set(['lava', 'crocodiles']),
    'bees': set(['sharks']),
    'lasers': set(['sharks', 'crocodiles']),
    'crocodiles': set(['piranhas', 'lasers'])
  }

我会这样称呼

public static void main(String[] args){
    System.out.println(bfs(myGraph, "crocodiles", "bees"));
}

到目前为止,这是我拥有的Java:

    public class BreadthFirstSearch{

    ///NOT DONE YET
    public static ArrayList<String> BFS(Map<String, String[]> graph, String start, String target) {
            List<String> path = new ArrayList<>();
            path.add(start);
            List<String> vertexAndPath = new ArrayList<>();
            vertexAndPath.add(start);
            vertexAndPath.add(path.get(0));
            ArrayList<String> queue = new ArrayList<>();
            queue.add(vertexAndPath.get(0));
            queue.add(vertexAndPath.get(1));
            Set<String> visited = new HashSet<String>();
            while(!queue.isEmpty()) {
                String currentVertex = queue.remove(0);
                String curVerValue = currentVertex;
                path.add(currentVertex);
                .
                .
                .
            }
        }
}

2 个答案:

答案 0 :(得分:2)

尽力翻译。让我提供我的代码,然后提供一个解释:

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对中的节点。这样,一旦定位了目标节点,我便一步一步地回溯了创建路径的过程,而不是为每个节点都建立了路径,而大多数路径最终都无济于事。这在时空上更有效;使用[] + [] O(n)列表串联运算符,Python使得破坏时间复杂性变得非常容易。

使用child => parent visited的HashMap也更易于用Java编写代码,它的重量不轻Pair / Tuple / struct可以方便地将不同类型的节点存储为队列中的节点。若要执行将2元素列表传递到队列中的Python操作,您必须编写自己的Pair类,使用两个ArrayDeques或避免泛型并使用强制转换,所有这些都很丑陋(尤其是最后一个,这也不安全)。

我在您的代码中注意到的另一个问题是将ArrayList用作队列。列表前面的插入和删除操作是O(n)操作,因为列表中的所有元素都必须在基础数组中向前或向后移动以保持顺序。 Java中的最佳队列结构是ArrayDeque,与Queue集合不同,它在两端都提供O(1)添加和删除,并且不是线程安全的。

类似地,在Python中,您会发现使用deque collection来获得最佳性能,该{{3}}提供了快速的popleft操作来满足所有排队需求。另外,在您的Python实现中,哈希中的每个键都指向一个set,这是可以的,但是当列表可以执行时,这似乎是不必要的结构(您已切换到Java中的原始数组)。如果您不操纵图而仅遍历邻居,这似乎是理想的。

请注意,此代码还假定每个节点都像输入一样,在表示该图的哈希中有一个键。如果计划在节点的哈希值中可能没有键的位置输入图,则需要确保graph.get(curr)containsKey包装,以避免崩溃。

另一个值得一提的假设:请确保您的图形不包含null,因为visited哈希依赖于null来表示孩子没有父母,并且是孩子的父母的起点。搜索。

答案 1 :(得分:0)

您需要创建一个单独的类来保存图的节点。这些节点不能都是静态的,因为它们都有唯一的顶点。从那里开始,其余部分非常相似。

public class Node {
    public String name;
    public ArrayList<Node> vertices;
    public void addEdge(Node node) {
        edges.add(node);
    }
}