简体   繁体   English

在Java中实现图形时出错

[英]Error in implementing a graph in Java

I am trying to create a graph in java using an ArrayList of LinkedList. 我正在尝试使用LinkedList的ArrayList在Java中创建图形。 I have implemented my own list. 我已经实现了自己的清单。 However, when I try to add connections between the vertices of the graph, I run into an endless loop. 但是,当我尝试在图的顶点之间添加连接时,遇到了一个无限循环。 I was debugging and I realized that this happens at the point when I am trying to add the element at the end of the LinkedList. 我正在调试,我意识到这是在尝试将元素添加到LinkedList末尾时发生的。 I am a beginner, and I don't see what's wrong with my List implementation. 我是一个初学者,我看不到List实现有什么问题。 Can anyone help? 有人可以帮忙吗?

import java.util.Stack;

// traverse the graph
public class GraphTraversal {


    public static void main(String[] args)
    {
        Graph graph=new Graph();
        initializeGraph(graph);
        graph.breadthFirstSearch();
    }

    public static void initializeGraph(Graph graph)
    {
        Node_Graph node1=new Node_Graph(1, false);
        Node_Graph node2=new Node_Graph(2, false);
        Node_Graph node3=new Node_Graph(3, false);
        Node_Graph node4=new Node_Graph(4, false);
        Node_Graph node5=new Node_Graph(5, false);
        Node_Graph node6=new Node_Graph(6, false);
        Node_Graph node7=new Node_Graph(7, false);
        Node_Graph node8=new Node_Graph(8, false);

        graph.addNode(node1);
        graph.addNode(node2);
        graph.addNode(node3);
        graph.addNode(node4);
        graph.addNode(node5);
        graph.addNode(node6);
        graph.addNode(node7);
        graph.addNode(node8);

        graph.makeConnection(node1, node2);
        graph.makeConnection(node1, node3);
        graph.makeConnection(node3, node4);
        graph.makeConnection(node3, node5);
        graph.makeConnection(node4, node5);
        graph.makeConnection(node4, node6);
        graph.makeConnection(node4, node8);
        graph.makeConnection(node4, node2);
        graph.makeConnection(node6, node5);
        graph.makeConnection(node8, node7);
        graph.makeConnection(node7, node2);
    }

}

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;

//Class for graph data structure
public class Graph {

    public ArrayList<List> nodes=new ArrayList<List>();


    public void addNode(Node_Graph n)
    {
        List new_node=new List();
        new_node.add(n);
        nodes.add(new_node);
    }

    public void makeConnection(Node_Graph node1, Node_Graph node2)
    {

        for(List list:nodes)
        {
            if(list.head.getId()==node1.getId())
            {
                list.add(node2);
                break;
            }
        }
    }

    public void breadthFirstSearch()
    {
        Stack<Node_Graph> traverse=new Stack<Node_Graph>();
        Node_Graph start=(nodes.get(0)).head;
        start.setVisited(true);
        traverse.push(start);
        while(traverse.empty())
        {
            Node_Graph popped=traverse.pop();
            System.out.println(popped.getId());
            List nextList= nodes.get(popped.getId());
            Node_Graph newElement=nextList.head;
            while(newElement.getNext()!=null)
            {
                newElement=newElement.getNext();
                if(!newElement.getVisited())
                {
                    newElement.setVisited(true);
                    traverse.push(newElement);
                }
            }

            if(!newElement.getVisited())
                traverse.push(newElement);
        }
    }
}

//linked list implementation
public class List{
    public Node_Graph head;
    public int size; 

    public List()
    {
        head=null;
        size=0;
    }

    public void add(Node_Graph element)
    {
        if(head==null)
        {
            head=element;
        }
        else
        {
            Node_Graph last=head;
            while(last.getNext()!=null)
            {
                last=last.getNext();
            }
            last.setNext(element);
        }
    }
}

//node of a graph
public class Node_Graph {

    private int id;
    private boolean visited;
    private Node_Graph next;

    public Node_Graph(int id,boolean visited)
    {
        this.id=id;
        this.visited=visited;
    }
    public void setId(int id)
    {
        this.id=id;
    }

    public int getId()
    {
        return id;
    }

    public void setVisited(boolean visited)
    {
        this.visited=visited;
    }

    public boolean getVisited()
    {
        return visited; 
    }

    public void setNext(Node_Graph next)
    {
        this.next=next;
    }

    public Node_Graph getNext()
    {
        return next; 
    }
}

The line graph.makeConnection(node4, node6); 线图graph.makeConnection(node4, node6); leads to an infinite loop because the next variable for node 4 is connected endlessly to node 5 导致无限循环,因为节点4的下一个变量与节点5无限连接

First thing I noticed is that the line graph.makeConnection(node3, node5); 我注意到的第一件事是线graph.makeConnection(node3, node5); is causing 4 to become connected to 5, which it shouldn't be. 导致4连接到5,这是不应该的。

I added toString methods to your list and node_graph classes to try to make it easier to understand what's going on; 我将toString方法添加到您的列表和node_graph类中,以使其更容易理解正在发生的事情。 here they are so you can try them: 在这里,您可以尝试一下:

List: 列表:

public String toString(){
  Node_Graph h = head;
  String s = "";
  while(h != null){
    s += "[" + h.toString() + "] ";
    h = h.next;
  }
  return s;
}

Node_Graph: Node_Graph:

public String toString(){
  String s = id + "";
  if(next != null)
    s += ", " + next.toString();
  return s;
}

Tracked down the error. 跟踪错误。 Let's start with this line: 让我们从这一行开始:

graph.makeConnection(node1, node3);

That causes the call: {1 -> 2,2 -> null}.add(3) So far so good. 这会导致调用: {1 -> 2,2 -> null}.add(3)到目前为止{1 -> 2,2 -> null}.add(3)顺利。

In add, You find the last element of the list: {2}, and set it's next to {3}. 另外,您将找到列表的最后一个元素:{2},并将其设置在{3}的旁边。 Thus, the list now looks like {1 -> 2 -> 3, 2 -> 3, 3}, when it should be {1 -> 2 -> 3, 2, 3}. 因此,列表现在看起来像{1-> 2-> 3,2-> 3,3},而列表应该是{1-> 2-> 3,2,3}。 The first list (incorrectly) implies that 1 is connected to 2 and 3, and 2 is connected to 3, whereas 2 should not be connected to 3, as the second list shows. 第一个列表(错误地)表示1连接到2和3,2连接到3,而2不应该连接到3,如第二个列表所示。 In your current scheme, this isn't possible, because the '2's are actually the same object with the same, singular next field. 在您当前的方案中,这是不可能的,因为“ 2”实际上是相同的对象,具有相同的,唯一的next字段。 It can't be {3} in the 1 element and {null} for itself. 它不能是1元素中的{3},本身不能是{null}。

Overall, you need to distinguish between the two "nexts". 总体而言,您需要区分两个“下一个”。 I believe your goal is that the next field in node_graph represents a node that this node is connected to, whereas the next in list represents the next node in the list, whether there is a connection or not. 我相信您的目标是,node_graph中的下一个字段表示该节点连接到的节点,而列表中的下一个字段表示列表中的下一个节点,无论是否存在连接。 You're trying to get two birds with one stone with that single next field and it's coming back to bite you with infinite recursion. 您正在尝试在下一个字段中用一块石头获得两只鸟,然后又将其无限次递归给您。

There are much cleaner ways to implement a graph - a hashmap (node -> list of neighbor nodes) is much much cleaner and saves you from dealing with all of this next business. 实现图有许多更简洁的方法-哈希图(节点->邻居节点列表)更加简洁,使您无需处理所有下一项业务。 If your main goal is to polish your graph algorithms, such as bfs/dfs-ing, you may just want to go with that. 如果您的主要目标是完善图形算法(例如bfs / dfs-ing),则可能只想这样做。

If you really want to implement a graph using lists though, you have some untangling to do. 但是,如果您确实想使用列表实现图,则需要进行一些整理。 I would advise removing the next field from the Node_Graph class entirely; 我建议完全从Node_Graph类中删除next字段。 the Node_Graph class should just worry about its own data, not maintaining list invariants. Node_Graph类应该只担心自己的数据,而不要维护列表不变式。 Then Make the list class have an inner wrapper class that holds a "this" (a Node_Graph instance) and a "next" (a Node_Wrapper) instance. 然后,使列表类具有一个内部包装器类,该包装器类包含一个“ this”(一个Node_Graph实例)和一个“ next”(一个Node_Wrapper)实例。 After all that is done, you can give your Node_Graph a neighbors field of type List, which will hold all of its accessible neighbors. 完成所有这些操作后,您可以为您的Node_Graph提供一个List类型的邻居字段,该字段将保存其所有可访问的邻居。

Here's a basic HashMap graph implementation that follows your pattern. 这是遵循您的模式的基本HashMap图形实现。 You don't need a list implementation either. 您也不需要列表实现。 No wrappers/nexts necessary: 无需包装器/下一步:

public class Node{

    public final Graph graph; //The graph this Node belongs to 
    private int id;
    private boolean visited;

    /** Constructs a Node with the given inputs. 
      * Also adds itself to g as part of construction */
    public Node(Graph g, int i, boolean v){
        graph = g;
        id = i;
        visited = v;
        graph.addNode(this);
    }

    public int getId(){
        return id;
    }

    public void setVisited(boolean v){
        visited = v;
    }

    //Getters for boolean fields usually follow the is<Field> pattern
    public boolean isVisited(){
        return visited;
    }

    /** Looks up the neighbors of this in the graph */
    public Set<Node> getNeighbors(){
        return graph.neighborsOf(this);
    }
}

public class Graph{

    private HashMap<Node, HashSet<Node>> graph;  //The actual graph. Maps a node -> its neighbors

    public Graph(){
        graph = new HashMap<Node, HashSet<Node>>();
    }

    /** Adds the node to this graph.
        If n is already in this graph, doesn't overwrite */
    public void addNode(Node n) throws IllegalArgumentException{
        if(n.graph != this) 
            throw new IllegalArgumentException(n + " belongs to " + n.graph ", not " + this);
        if(! graph.contains(n))
            graph.put(n, new HashSet<Node>());
    }

    /** Returns the neighbors of the given node. 
      * Returns null if the node isn't in this graph */
    public Set<Node> neighborsOf(Node n){
        if(! graph.contains(n))
            return null;

        return graph.get(n);
    }

    /** Connects source to sink. Also adds both to graph if they aren't there yet */
    public void makeConnection(Node source, Node sink){
        //Make sure source and sink belong to this graph first
        addNode(source);
        addNode(sink);

        //Make the connection by adding sink to source's associated hashset
        graph.get(source).add(sink);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM