package de.master.core.graph.base;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
* A basic graph implementation. this graph is not directed, cyclic and
* weighted.
*
* @author m.kandora
*
* @param <T>
* the containing generic data type
*/
public class Graph<T>
{
/**
* A set of all nodes in this graph
*/
protected List<Node<T>> nodes;
/**
* A map of all weights between all nodes
*/
protected Map<Node<T>, Map<Node<T>, Double>> weights;
/**
* A mapping of predecessors to a given node
*/
protected Map<Node<T>, Node<T>> predecessors;
/**
* the first node in the graph.
*/
protected Node<T> first;
/**
* this is the node pointer, where successive nodes will be inserted as
* children
*/
protected Node<T> previous;
/**
* Creates a new empty graph
*/
public Graph()
{
nodes = new LinkedList<Node<T>>();
weights = new HashMap<Node<T>, Map<Node<T>, Double>>();
predecessors = new HashMap<Node<T>, Node<T>>();
}
/**
* Inserts a specific node into the graph. Please note that the inserted
* node will be the child of an existing predecessor. if no predecessor is
* available the child must be the first node. all successive nodes will be
* appended to this node.
*
* @param weight
* the weight of the inserted child
* @param node
* the node to be inserted
*/
public void insert(final double weight, final Node<T> node)
{
if (previous == null)
{
previous = node;
first = previous;
nodes.add(previous);
}
else
{
updateChildren(node);
if(!nodes.contains(node))
{
nodes.add(node);
}
predecessors.put(node, previous);
insertWeight(previous, node, weight);
}
}
/**
* inserts node into previous node and vice versa
* @param node
*/
private void updateChildren(final Node<T> node)
{
List<Node<T>> children = previous.getNeighbours();
if (!children.contains(node) && !node.equals(previous))
{
children.add(node);
List<Node<T>> other = node.getNeighbours();
if(!other.contains(previous))
{
other.add(previous);
}
}
}
/**
* Inserts a specific child after the node at. Inserts an edge
*
* @param weight
* the weight for this child
* @param node
* the node to be inserted
* @param at
* the node after the child is to be inserted
*/
public void insertAt(final double weight, final Node<T> node, final Node<T> at)
{
previous = at;
insert(weight, node);
}
/**
* Removes a node from graph
*
* @param node
* the node to remove
*/
public void remove(final Node<T> node)
{
if (node.equals(previous))
{
Node<T> tmpPre = predecessors.get(previous);
if (tmpPre != null)
{
previous = tmpPre;
}
}
if (node.equals(first))
{
if (nodes.size() == 1)
{
nodes.remove(first);
} else
{
if (nodes.contains(first))
{
// find maximum weighted child
Node<T> n = maxWeight(first.getNeighbours(), first);
for (Node<T> child : first.getNeighbours())
{
if (!child.equals(n))
{
n.addNode(child);
predecessors.put(child, n);
double weight = weights.get(first).get(child);
removeWeight(first, child);
insertWeight(n, child, weight);
}
}
} else
{
System.err.println("this should not happy.");
}
}
}
nodes.remove(node);
Node<T> pre = null;
if (predecessors.containsKey(node))
{
predecessors.remove(node);
}
if (predecessors.containsValue(node))
{
for (Node<T> n : predecessors.keySet())
{
if (predecessors.get(n) != null
&& predecessors.get(n).equals(node))
{
pre = n;
}
}
predecessors.remove(pre);
}
removeWeight(pre, node);
// remove also from neighbours
Stack<Node> buffer = new Stack<Node>();
for(Node n : node.getNeighbours())
{
buffer.push(n);
}
while(!buffer.isEmpty()) {
buffer.pop().removeNode(node);
}
}
/**
* Inserts a weight between two nodes. the first node is getting checked
* against an existing entry map. if the entry map is not empty or null, the
* second parameter gets inserted with the current weight.
*
* if both parameters have no links at all, a new map is being created and
* both paramters, the appropriate weights and the reference other node is
* being inserted into {@link Graph#weights}
*
*
* @param a
* @param b
* @param weight
*/
protected void insertWeight(Node<T> a, Node<T> b, double weight)
{
Map<Node<T>, Double> link = null;
if (weights.get(a) != null)
{
link = weights.get(a);
link.put(b, weight);
weights.put(a, link);
}
else {
link = new HashMap<Node<T>, Double>();
link.put(b, weight);
weights.put(a, link);
}
if (weights.get(b) != null)
{
link = weights.get(b);
link.put(a, weight);
weights.put(b, link);
}
else
{
link= new HashMap<Node<T>, Double>();
link.put(a, weight);
weights.put(b, link);
}
}
/**
* Removes a weight between two nodes
*
* @param a
* @param b
*/
protected void removeWeight(final Node<T> a, final Node<T> b)
{
try
{
if (a != null)
weights.get(a).remove(b);
if (b != null)
weights.get(b).remove(a);
} catch (Exception e)
{
// no need to throw an exception, since the map does not
// permits null values
}
}
/**
* Returns the child of parent with a maximum weighted edge
*
* @param children
* the children to look for the maximum weighted child
* @param parent
* the parent to create an edge with one selected child
* @return the maximum weighted child of parent
*/
protected Node<T> maxWeight(final List<Node<T>> children,
final Node<T> parent)
{
Node<T> max = null;
double v = 0;
for (Node<T> node : children)
{
if (v < weight(parent, node))
{
v = weight(parent, node);
max = node;
}
}
return max;
}
/**
* Returns the associated weight between two nodes
*
* @param a
* the first node
* @param b
* the second node
* @return the weight weight between a and b
*/
public Double weight(final Node<T> a, Node<T> b)
{
Double v = 0.0;
return weights.get(a) != null ? weights.get(a).get(b) : 0;
}
/**
* Returns the number of all nodes inside the graph
*
* @return the number of nodes
*/
public int getNodeCount()
{
return nodes.size();
}
/**
* Returns all nodes from graph
*
* @return a set with all nodes insode this graph
*/
public List<Node<T>> getNodes()
{
return nodes;
}
////<<<<<<< .mine
// public Node<T> getNodeByID(int id)
// {
// for (Node node : nodes)
// {
// if (node.getId() == id)
// return node;
// }
//
// return null;
// }
//
// protected List<Node<T>> getNeighbours(Node<T> node) {
// List<Node<T>> list = new ArrayList<>();
// if(weights.get(node) != null) {
// list.addAll(weights.get(node).keySet());
// }
// return list;
// }
////=======
////d// protected List<Node<T>> getNeighbours(Node<T> node) {
////// List<Node<T>> list = new ArrayList<>();
////// if(weights.get(node) != null) {
////// list.addAll(weights.get(node).keySet());
////// }
////// return list;
////// }
////>>>>>>> .r583
/**
* returns the first node in this graph
*
* @return
*/
public Node<T> getFirst()
{
return first;
}
/**
* Returns the previous node, that is the current node pointer
*
* @return the previous node
*/
public Node<T> getPrevious()
{
return previous;
}
/**
* Sets the previous node, that is any new inserted nodes will be set as her
* child
*
* @param node
* the node to set as new predecessor
*/
public void setPrevious(final Node<T> node)
{
if (!nodes.contains(node))
throw new RuntimeException(
"Node "
+ node
+ "cannot be set as previous, since its not part of the graph!");
this.previous = node;
}
/**
* Clears this graph. eg. removes all children, weights etc..
*/
public void clear()
{
nodes.clear();
predecessors.clear();
weights.clear();
first = null;
previous = null;
}
/**
* Returns true if this graph contains no nodes
*
*/
public boolean isEmpty()
{
return getNodeCount() == 0;
}
/**
* Returns the predecessor (aka the parent) of Node n.
*
* @param node
* the node to find the predeccessor
* @return the predeccessor
*/
public Node<T> getPredecessor(final Node<T> node)
{
return predecessors.get(node);
}
}