1

I hope someone can help with the issue I am having. First of all, I tried to remove as much code as I can that wasn't causing the issue.

My problem is this: When I run the program everything runs perfectly until I create a graph with about 130 nodes. Once it hits 130+ nodes the program will run forever in an infinite loop.

I try running the program with 135 nodes at 15 for the desired graph density.

To give some context I am working on research simulations and for this I am creating random graphs and building spanning trees using BFS.

My problem arises during the creation of the Spanning Tree.

Copy and paste code and compile using javac MMB.java all in one file.

import java.util.*;

/**
 *  Custom type Set used to differentiate nodes in the FAST and SLOW sets
*/
enum Set {
    FAST, SLOW;
}

/**
 *  Custom node class used to store our spanning tree
*/
class Node<T> {

    private T data;
    private Node<T> parent;
    private List<Node<T>> children;
    private int level;
    private int rank;
    private Set set;

    // constructor for root Nodes and stand alone nodes
    public Node(T data){
        this.data = data;
        this.parent = null;
        this.level = 0;
        this.rank = Integer.MIN_VALUE;
        this.children = new ArrayList<Node<T>>();
    }

    // constructor for all non root nodes
    public Node(T data, Node<T> parent){
        this.data = data;
        this.setParent(parent);
        this.level = (parent.getLevel()) + 1;
        this.rank = Integer.MIN_VALUE;
        this.children = new ArrayList<Node<T>>();
    }

    // get data
    public T getData(){
        return this.data;
    }

    // set data
    public void setData(T data){
        this.data = data;
    }

    // add child node
    public void addChild(Node<T> child){
        children.add(child);
    }

    // remove child node
    public void removeChild(Node<T> child){
        children.remove(child);
    }

    // get rank
    public int getRank(){
        return this.rank;
    }

    // set rank
    public void setRank(int rank){
        this.rank = rank;
    }

    // get parent
    public Node<T> getParent(){
        return this.parent;
    }

    // set parent - updates parent node to have child
    public void setParent(Node<T> parent){
        this.parent = parent;
        parent.addChild(this);
    }

    // returns level of a Node
    public int getLevel(){
        return this.level;
    }

    // returns a list of children of a given node
    public List<Node<T>> getChildren(){
        return this.children;
    }

    // set the Set a node is in
    public void setSet(Set set){
        this.set = set;
    }

    // get the Set a node is in
    public Set getSet(){
        return this.set;
    }

    // returns the tree as a list of nodes using DFS traversal
    public List<Node<T>> treeToList(){
        List<Node<T>> list = new LinkedList<Node<T>>();
        List<Node<T>> visitedNodes = new LinkedList<Node<T>>();

        list.add(this);
        while(list.size() > 0){
            Node<T> currentNode = list.get(list.size() - 1);
            List<Node<T>> currentNodesChildren = currentNode.getChildren();
            if(!visitedNodes.contains(currentNode)){
                for(Node<T> n : currentNodesChildren){
                    list.add(n);
                }
                visitedNodes.add(currentNode);
            }
            else {
                list.remove(currentNode);
            }
        }
        return visitedNodes;
    }

    // returns the number of levels in the tree
    // Note: levels start at 0
    public int numberOfLevels(){
        List<Node<T>> list = this.treeToList();
        int maxLevel = 0;
        for(Node<T> n : list)
            if(n.getLevel() > maxLevel)
                maxLevel = n.getLevel();
        return maxLevel + 1;
    }

    // returns the max rank in the tree
    public int maxRank(){
        List<Node<T>> list = this.treeToList();
        int maxRank = 0;
        for(Node<T> n : list)
            if(n.getRank() > maxRank)
                maxRank = n.getRank();
        return maxRank;
    }

    // returns a list of nodes with a given rank and level in the FAST set
    public List<Node<T>> nodeRankLevelSubset(int rank, int level){
        List<Node<T>> list = this.treeToList();
        List<Node<T>> subset = new LinkedList<Node<T>>();
        for(Node<T> n : list)
            if(n.getRank() == rank && n.getLevel() == level && n.getSet() == Set.FAST)
                subset.add(n);
        return subset;
    }

    // Print All
    public void printAll(){
        List<Node<T>> list = this.treeToList();
        for(Node<T> n : list){
            System.out.println("{");
            System.out.println(" \"data\": " + n.getData() + ",");
            System.out.println(" \"level\": " + n.getLevel() + ",");
            System.out.println(" \"rank\": " + n.getRank() + ",");
            switch(n.getSet()){
                case FAST:
                    System.out.println(" \"set\": \"FAST\"");
                    break;
                case SLOW:
                    System.out.println(" \"set\": \"SLOW\"");
                    break;
            }
            System.out.print(" \"parent\": ");
            if(n.getParent() != null)
                System.out.println(n.getParent().getData() + ",");
            else
                System.out.println(" ,");
            System.out.print(" \"children\": [");
            for(Node<T> cn : n.getChildren()){
                System.out.print(cn.getData() + ",");
            }
            System.out.println("]\n}");
        }
    }
    // BFS to print
    public void printTree(){
        List<Node<T>> discoveredNodes = new LinkedList<Node<T>>();
        List<Node<T>> queue = new LinkedList<Node<T>>();
        List<Node<T>> children;
        Node<T> currentNode;
        queue.add(this);
        discoveredNodes.add(this);
        while(queue.size() > 0){
            currentNode = queue.remove(0);
            children = currentNode.getChildren();
            System.out.print("\n" + currentNode.getData() + ": ");
            for(Node<T> n : children){
                queue.add(n);
                discoveredNodes.add(n);
                System.out.print(n.getData() + " " + " Rank: " + n.getRank() + " ");
            }
        }
        System.out.print("\n");
    }
}

public class MMB {
    // boolean 2D array used to make the edges in a random graph
    public static boolean[][] randomGraph;
    // custom Node class used to store our BFS spanning tree
    public static Node<Integer> spanningTree;

    public static void main(String[] args){
        int numberOfNodes, graphDensity;

        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter the desired number of Nodes: ");
        numberOfNodes = scanner.nextInt();
        System.out.print("Enter the desired graph density: ");
        graphDensity = scanner.nextInt();

        randomGraph = randomGraph(numberOfNodes, graphDensity);

        /* Print Out Graph */
        for(int i = 0; i < randomGraph.length; i++){
            System.out.print(i + " ");
            for(int j = 0; j < randomGraph.length; j++){
                System.out.printf(" " + randomGraph[i][j] + " ");
            }
            System.out.println("");
        }
        System.out.println("");
        System.out.println("HERE - CREATED GRAPH");
        spanningTree = spanningTree(0);
        System.out.println("HERE - CREATED SPAnnING TREE");
        // rankNodes(spanningTree, spanningTree.numberOfLevels());
        // System.out.println("HERE - FIRST RANK");
        // determineSet(spanningTree);
        // System.out.println("HERE - DETERMINE SET");
        // //spanningTree.printTree();
        // reRankNodes(spanningTree);
        // System.out.println("HERE - RERANK NODES");
        // //spanningTree.printTree();
        // spanningTree.printAll();
    }


    /**
     *  Create an undirected graph at random
     *  A 2D boolean array will represent the edges between nodes
     *  @param  numberOfNodes   number of nodes in the graph
     *  @param  graphDensity    integer percentage of graph density
    */
    public static boolean[][] randomGraph(int numberOfNodes, int graphDensity){
        boolean[][] graph = new boolean[numberOfNodes][numberOfNodes];
        Random randomNumber = new Random();

        boolean hasEdge;
        for(int i = 0; i < numberOfNodes; i++){
            hasEdge = false;
            for(int j = 0; j < numberOfNodes; j++){
                // i != j ensures no loops
                if(i != j && (randomNumber.nextInt(100) + 1) < graphDensity){
                    graph[i][j] = true;
                    graph[j][i] = true;
                    hasEdge = true;
                }
            }
            // to ensure no disconnected nodes, keep track with hasEdge
            // if no edge exists create a random one
            int randomNum;
            while(!hasEdge){
                if((randomNum = randomNumber.nextInt(numberOfNodes)) != i){
                    graph[i][randomNum] = true;
                    graph[randomNum][i] = true;
                    hasEdge = true;
                }
            }
        }
        return graph;
    }


    /**
     *  Create a Spanning Tree from an undirected graph using BFS
     *  A custom Node structure will represent our spanning tree
     *  @param  root    root of undirected graph from 0 to numberOfNodes-1
    */
    public static Node<Integer> spanningTree(int root){
        Node<Integer> tree = new Node<Integer>(root);
        Node<Integer> currentNode;
        Integer currentNodeData;

        LinkedList<Node<Integer>> discoveredNodes = new LinkedList<Node<Integer>>();
        LinkedList<Node<Integer>> queue = new LinkedList<Node<Integer>>();

        queue.add(tree);
        discoveredNodes.add(tree);
        while(queue.size() > 0){
            currentNode = queue.removeFirst();
            currentNodeData = currentNode.getData();
            for(int i = 0; i < randomGraph.length; i++){
                if(randomGraph[currentNodeData][i] && !listContainsNode(discoveredNodes, i)){
                    Node<Integer> newNode = new Node<Integer>(i, currentNode);
                    queue.add(newNode);
                    discoveredNodes.add(newNode);
                }
            }
        }
        return tree;
    }

    /* Helper Methods */
    // search a list of Nodes for a value
    public static boolean listContainsNode(List<Node<Integer>> list, Integer data){
        for(Node<Integer> n : list)
            if(n.getData() == data)
                return true;
        return false;
    }
}
Kevin Zaki
  • 136
  • 1
  • 2
  • 9

2 Answers2

1

In cauda venenum

/* Helper Methods */
// search a list of Nodes for a value
public static boolean listContainsNode(List<Node<Integer>> list, Integer data){
    for(Node<Integer> n : list)
        if(n.getData() == data)  // <-- Can you spot the bug?
            return true;
    return false;
}

The problem is that you compare Integer's with ==. With numbers lower than 128 it works because integers are interned, but then the comparison on objects doesn't work anymore.

Just use:

if (n.getData().equals(data))

to compare node contents (and keep up writing great code ;) - it's not easy to see such quality in StackOverflow's questions)

Raffaele
  • 19,761
  • 5
  • 42
  • 81
  • Thanks for the quick reply and answer. It worked. When I started working on this I noted to myself to watch out for errors caused by Integer but it slipped my mind. Thanks for the code quality compliment. I try to follow the standards as much as possible. – Kevin Zaki Mar 02 '15 at 23:41
1

Well, I found a potential dangerous code, if you matrix would not be squared, so you should use

for(int i = 0; i < randomGraph[currentNodeData].length; i++){

instead of

for(int i = 0; i < randomGraph.length; i++){

And I think that I maybe found the nested exception :).

If you are using Integer instead of int, you have to care about == operator. Java makes static Integers for values -127 to 128, but when you reach it, it does not work anymore!!

This code

    Integer x;
    Integer y;

    x = 111;
    y = 111;

    System.out.println(x == y);

    x = 130;
    y = 130;

    System.out.println(x == y);

Is having this output

true
false

Using equals is safe however, this code :

    Integer x;
    Integer y;

    x = 111;
    y = 111;

    System.out.println(x.equals(y));

    x = 130;
    y = 130;

    System.out.println(x.equals(y));

Is having this output

true
true
libik
  • 19,204
  • 9
  • 35
  • 72
  • Thanks for the help. As for the matrix, I don't think it would matter in this particular example but I am going to change it because it is better coding practice. Also, you are right that was the issue. I gave the answer to Raff above because his was first. Thanks for your help! – Kevin Zaki Mar 02 '15 at 23:43