0

I am working on a project in Java that uses a weighted graph to represent a complex family tree, but I have run into problems implementing the basic functionality of the graph. I have 3 classes, a Graph class, a Node class that is just a wrapper around some generic member, and a Tester class. The Graph class maintains a list of Node objects, and each Node keeps a list of the nodes adjacent to it and the weights to each adjacent node. My problem is that, when I create a simple graph that uses spouses and children, I cannot add a child to a node and the parent's spouse. However, if I can add a child to a node, and then a different child to the spouse. For example, I can do this:

A --- B
|     |
C     D

but I cannot do this:

A --- B
   |
   C

By cannot do this, I mean that this structure is reflected when I build the graph, but not when I print it. I am stumped, as I cannot find any problems in my code, but I included all 3 classes below. Thank you in advance.

Graph.java

import java.util.HashSet;

public class Graph<T> {
    public static double PARENT_TO_CHILD = 1.0;
    public static double CHILD_TO_PARENT = -1.0;
    public static double SPOUSE_TO_SPOUSE = -0.1;
    private Node<T> root;
    private HashSet<Node<T>> nodes;

    /**
     * Creates a graph with the given Node as the root.
     */
    public Graph(T root){
        this(new Node<T>(root));
    }

    public Graph(Node<T> root){
        this.root = root;

        nodes = new HashSet<>();
        nodes.add(root);
    }

    /**
     * Adds the given Node or T as a child of the second Node or T.
     *
     * @param child         the Node or T to be added as a child
     * @param parent        the Node or T to add the child to
     */
    public void addChild(T child, T parent){
        addChild(new Node<T>(child), findNode(parent));
    }

    public void addChild(Node<T> child, Node<T> parent){
        System.out.println("Adding " + child.toString() + " as a child of " + parent.toString());
        addEdge(parent, child, Graph.PARENT_TO_CHILD);
    }

    /**
     * Adds the given Node or T as a parent of the second Node or T.
     *
     * @param parent       the Node or T to be added as a parent
     * @param child        the Node or T to add the parent to
     */
    public void addParent(T parent, T child){
        addParent(new Node<T>(parent), findNode(child));
    }

    public void addParent(Node<T> parent, Node<T> child){
        System.out.println("Adding " + parent.toString() + " as a parent of " + child.toString());
        addEdge(parent, child, Graph.PARENT_TO_CHILD);
    }

    /**
     * Adds the given node as a spouse of the node with the given name.
     *
     * @param spouse  the node to be added as a spouse
     * @param node    the Node to add the spouse to
     */

    public void addSpouse(T spouse, T member){
        addSpouse(new Node<T>(spouse), findNode(member));
    }

    public void addSpouse(Node<T> spouse, Node<T> node){
        System.out.println("Adding " + spouse.toString() + " as a spouse of " + node.toString());
        addEdge(node, spouse, Graph.SPOUSE_TO_SPOUSE);
    }

    /**
     * Adds an edge between two Nodes with a weight in the Node objects.
     *
     * @param n1      the first vertex of the edge
     * @param n2      the second vertex of the edge
     * @param weight  the weight from n1 to n2, inverted when linking the other way
     */
    public void addEdge(Node<T> n1, Node<T> n2, double weight){
        System.out.println("Adding an edge between " + n1.toString() + " and " + n2.toString() + " with weight " + weight);
        n1.addEdge(n2, weight);
        n2.addEdge(n1, -weight);

        nodes.add(n1);
        nodes.add(n2);
    }

    /**
     * Returns a reference to the Node in the graph with the given member.
     *
     * @param member  the member of the Node to find
     * @return        a reference to the found Node, or null if it was not found
     */

    public Node<T> findNode(T member){
        System.out.println("Looking for a Node with member: " + member.toString());
        for (Node<T> node : nodes)
            if (node.equalMembers(member))
                return node;
        System.out.println("Error: Could not find a node with member " + member.toString() + " in tree.");
        return null;
    }

    /**
     * Prints the adjacency lists of each node in the graph and their weight.
     */
    public void print(){
        System.out.println("\n==================================================");
        for (Node<T> node : nodes){
            System.out.println("Adjacency list of " + node.toString());
            System.out.println("    " + node.adjacentNodesToString());
        }
        System.out.println("==================================================\n");
    }
}

Node.java

import java.util.HashMap;

public class Node<T> {
    private HashMap<Node<T>, Double> adjNodes;
    private T member;

    public Node(T member){
        this.member = member;
        adjNodes = new HashMap<>();
    }

    public void addEdge(Node<T> vertex, double weight){
        System.out.println("Adding nodewise edge from " + toString() + " to " + vertex.toString() + " with weight " + weight);
        adjNodes.put(vertex, weight);

        System.out.println("Adjacent nodes of " + toString());
        System.out.println("    " + adjacentNodesToString());
    }

    public double getWeight(Node<T> vertex){
        return adjNodes.get(vertex);
    }

    public String adjacentNodesToString(){
        if (adjNodes.size() == 0)
            return "None";
        String adj = "";
        for (Node<T> adjNode : adjNodes.keySet())
            adj += adjNode.toString() + " (weight: " + getWeight(adjNode) + ")    ";
        return adj.substring(0, adj.length()-4);
    }

    public boolean equalMembers(T otherMem){
        return member.equals(otherMem);
    }

    @Override
    public boolean equals(Object obj){
        if (obj == null){
            return false;
        }
        if (obj instanceof Node){
            Node other = (Node) obj;
            return member.equals(other.member);
        }
        return false;
    }

    @Override
    public int hashCode(){
        return member.hashCode();
    }

    @Override
    public String toString(){
        return member.toString() + " (" + hashCode() + ")" ;
    }
}

Tester.java

public class Tester {
    public static void main(String[] args){
        graphTest();
    }

    public static void graphTest(){
        // Create the graph with 1 as the root
        Graph<Integer> g = new Graph<>(1);
        g.print();

        // Add 2 as a spouse of 1
        g.addSpouse(2, 1);
        g.print();

        // Add 3 as a child of 1
        g.addChild(3, 1);
        g.print();

        // Add 3 as a child of 2
        g.addChild(3, 2); // If this was g.addChild(4, 2) it would be right
        g.print();
    }
}

Output

==================================================
Adjacency list of 1 (1)
    None
==================================================

Looking for a Node with member: 1
Adding 2 (2) as a child of 1 (1)
Adding an edge between 1 (1) and 2 (2) with weight 1.0
Adding nodewise edge from 1 (1) to 2 (2) with weight 1.0
Adjacent nodes of 1 (1)
    2 (2) (weight: 1.0)
Adding nodewise edge from 2 (2) to 1 (1) with weight -1.0
Adjacent nodes of 2 (2)
    1 (1) (weight: -1.0)

==================================================
Adjacency list of 1 (1)
    2 (2) (weight: 1.0)
Adjacency list of 2 (2)
    1 (1) (weight: -1.0)
==================================================

Looking for a Node with member: 1
Adding 3 (3) as a child of 1 (1)
Adding an edge between 1 (1) and 3 (3) with weight 1.0
Adding nodewise edge from 1 (1) to 3 (3) with weight 1.0
Adjacent nodes of 1 (1)
    2 (2) (weight: 1.0)    3 (3) (weight: 1.0)
Adding nodewise edge from 3 (3) to 1 (1) with weight -1.0
Adjacent nodes of 3 (3)
    1 (1) (weight: -1.0)

==================================================
Adjacency list of 1 (1)
    2 (2) (weight: 1.0)    3 (3) (weight: 1.0)
Adjacency list of 2 (2)
    1 (1) (weight: -1.0)
Adjacency list of 3 (3)
    1 (1) (weight: -1.0)
==================================================

Looking for a Node with member: 2
Adding 4 (4) as a child of 2 (2)
Adding an edge between 2 (2) and 4 (4) with weight 1.0
Adding nodewise edge from 2 (2) to 4 (4) with weight 1.0
Adjacent nodes of 2 (2)
    1 (1) (weight: -1.0)    4 (4) (weight: 1.0)
Adding nodewise edge from 4 (4) to 2 (2) with weight -1.0
Adjacent nodes of 4 (4)
    2 (2) (weight: -1.0)

==================================================
Adjacency list of 1 (1)
    2 (2) (weight: 1.0)    3 (3) (weight: 1.0)
Adjacency list of 2 (2)
    1 (1) (weight: -1.0)    4 (4) (weight: 1.0)
Adjacency list of 3 (3)
    1 (1) (weight: -1.0)
Adjacency list of 4 (4)
    2 (2) (weight: -1.0)
==================================================
  • 1
    Your data structure cannot be used to model the two-parents-one-child relationship. You must have a node representing the "spouse" relationship, and the children of that union are children of the relationship node. Time to rethink the data structures to correctly model the real world. Also, it's not clear what edge weights have to do with modeling family trees. – Jim Garrison Jan 21 '18 at 04:22
  • @JimGarrison Weights reflect the generational difference between connected nodes. Parent to child has a positive weight because the generation increases, child to parent is negative, spouse to spouse rounds off to zero. I updated the structure to us relationships and found that the error was when I linked nodes that were already in the set of used nodes, I didn't use the existing node. –  Jan 29 '18 at 19:51

0 Answers0