All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graphstream.algorithm.Dijkstra Maven / Gradle / Ivy

Go to download

The GraphStream library. With GraphStream you deal with graphs. Static and Dynamic. You create them from scratch, from a file or any source. You display and render them. This package contains algorithms and generators.

There is a newer version: 2.0
Show newest version
/*
 * Copyright 2006 - 2013
 *     Stefan Balev     
 *     Julien Baudry    
 *     Antoine Dutot    
 *     Yoann Pigné      
 *     Guilhelm Savin   
 * 
 * This file is part of GraphStream .
 * 
 * GraphStream is a library whose purpose is to handle static or dynamic
 * graph, create them from scratch, file or any source and display them.
 * 
 * This program is free software distributed under the terms of two licenses, the
 * CeCILL-C license that fits European law, and the GNU Lesser General Public
 * License. You can  use, modify and/ or redistribute the software under the terms
 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
 * URL  or under the terms of the GNU LGPL as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
 */
package org.graphstream.algorithm;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

import org.graphstream.algorithm.util.FibonacciHeap;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.Path;

/**
 * 

* Dijkstra's algorithm computes the shortest paths from a given node called * source to all the other nodes in a graph. It produces a shortest path tree * rooted in the source. This algorithm works only for nonnegative * lengths. *

* *

* This implementation uses internally Fibonacci Heap, a data structure that * makes it run faster for big graphs. *

* *

Length of a path

* *

* Traditionally the length of a path is defined as the sum of the lengths of * its edges. This implementation allows to take into account also the "lengths" * of the nodes. This is done by a parameter of type {@link Element} passed in * the constructors. *

* *

* The lengths of individual elements (edges or/and nodes) are defined using * another constructor parameter called {@code lengthAttribute}. If this * parameter is {@code null}, the elements are considered to have unit lengths. * In other words, the length of a path is the number of its edges or/and nodes. * If the parameter is not null, the elements are supposed to have a numeric * attribute named {@code lengthAttribute} used to store their lengths. *

* *

Solutions

* *

* Internal solution data is stored in attributes of the nodes of the underlying * graph. The name of this attribute is another constructor parameter called * {@code resultAttribute}. This name must be specified in order to avoid * conflicts with existing attributes, but also to distinguish between solutions * produced by different instances of this class working on the same graph (for * example when computing shortest paths from two different sources). If not * specified, a unique name is chosen automatically based on the hash code of * the Dijkstra instance. The attributes store opaque internal objects and must * not be accessed, modified or deleted. The only way to retrieve the solution * is using different solution access methods. *

* *

Usage

* *

* A typical usage of this class involves the following steps: *

*
    *
  • Instantiation using one of the constructors with appropriate parameters
  • *
  • Initialization of the algorithm using {@link #init(Graph)}
  • *
  • Computation of the shortest paths using {@link #compute()}
  • *
  • Retrieving the solution using different solution access methods
  • *
  • Cleaning up using {@link #clear()}
  • *
* *

* Note that if the graph changes after the call of {@link #compute()} the * computed solution is no longer valid. In this case the behavior of the * different solution access methods is undefined. *

* *

Example

* *
 * Graph graph = ...;
 * 
 * // Edge lengths are stored in an attribute called "length"
 * // The length of a path is the sum of the lengths of its edges
 * // The algorithm will store its results in attribute called "result"
 * Dijkstra dijkstra = new Dijkstra(Dijkstra.Element.edge, "result", "length");
 * 	
 * // Compute the shortest paths in g from A to all nodes
 * dijkstra.init(graph);
 * dijkstra.setSource(graph.getNode("A"));
 * dijkstra.compute();
 * 	
 * // Print the lengths of all the shortest paths
 * for (Node node : graph)
 *     System.out.printf("%s->%s:%6.2f%n", dijkstra.getSource(), node, dijkstra.getPathLength(node));
 * 	
 * // Color in blue all the nodes on the shortest path form A to B
 * for (Node node : dijkstra.getPathNodes(graph.getNode("B")))
 *     node.addAttribute("ui.style", "fill-color: blue;");
 * 	
 * // Color in red all the edges in the shortest path tree
 * for (Edge edge : dijkstra.getTreeEdges())
 *     edge.addAttribute("ui.style", "fill-color: red;");
 * 
 * // Print the shortest path from A to B
 * System.out.println(dijkstra.getPath(graph.getNode("B"));
 * 
 * // Build a list containing the nodes in the shortest path from A to B
 * // Note that nodes are added at the beginning of the list
 * // because the iterator traverses them in reverse order, from B to A
 * List <Node> list1 = new ArrayList<Node>();
 * for (Node node : dijkstra.getPathNodes(graph.getNode("B")))
 *     list1.add(0, node);
 * 
 * // A shorter but less efficient way to do the same thing
 * List<Node> list2 = dijkstra.getPath(graph.getNode("B")).getNodePath();
 * 
* * @author Stefan Balev */ public class Dijkstra extends AbstractSpanningTree { protected static class Data { FibonacciHeap.Node fn; Edge edgeFromParent; double distance; } /** * This enumeration is used to specify how the length of a path is computed * * @author Stefan Balev */ public static enum Element { /** * The length of a path is the sum of the lengths of its edges. */ EDGE, /** * The length of a path is the sum of the lengths of its nodes. */ NODE, /** * The length of a path is the sum of the lengths of its edges and * nodes. */ EDGE_AND_NODE; } protected Element element; protected String resultAttribute; protected String lengthAttribute; protected Node source; // *** Helpers *** protected double getLength(Edge edge, Node dest) { double lenght = 0; if (element != Element.NODE) lenght += lengthAttribute == null ? 1 : edge .getNumber(lengthAttribute); if (element != Element.EDGE) lenght += lengthAttribute == null ? 1 : dest .getNumber(lengthAttribute); if (lenght < 0) throw new IllegalStateException("Edge " + edge.getId() + " has negative lenght " + lenght); return lenght; } protected double getSourceLength() { if (element == Element.EDGE) return 0; return lengthAttribute == null ? 1 : source.getNumber(lengthAttribute); } // *** Constructors *** /** * Constructs an instance with the specified parameters. The edges of the shortest path tree are not tagged. * * @param element * Graph elements (edges or/and nodes) used to compute the path * lengths. If {@code null}, the length of the path is computed * using edges. * @param resultAttribute * Attribute name used to store internal solution data in the * nodes of the graph. If {@code null}, a unique name is chosen * automatically. * @param lengthAttribute * Attribute name used to define individual element lengths. If * {@code null} the length of the elements is considered to be * one. */ public Dijkstra(Element element, String resultAttribute, String lengthAttribute) { this(element, resultAttribute, lengthAttribute, null, null, null); } /** * Constructs an instance in which the length of the path is considered to * be the number of edges. Unique result attribute is chosen automatically. The edges of the shortest path tree are not tagged. */ public Dijkstra() { this(null, null, null, null, null, null); } /** * Constructs an instance with the specified parameters. * * @param element * Graph elements (edges or/and nodes) used to compute the path * lengths. If {@code null}, the length of the path is computed * using edges. * @param resultAttribute * Attribute name used to store internal solution data in the * nodes of the graph. If {@code null}, a unique name is chosen * automatically. * @param lengthAttribute * Attribute name used to define individual element lengths. If * {@code null} the length of the elements is considered to be * one. * @param flagAttribute * attribute used to set if an edge is in the spanning tree * @param flagOn * value of the flagAttribute if edge is in the spanning * tree * @param flagOff * value of the flagAttribute if edge is not in the * spanning tree */ public Dijkstra(Element element, String resultAttribute, String lengthAttribute, String flagAttribute, Object flagOn, Object flagOff) { super(flagAttribute, flagOn, flagOff); this.element = element == null ? Element.EDGE : element; this.resultAttribute = resultAttribute == null ? toString() + "_result_" : resultAttribute; this.lengthAttribute = lengthAttribute; graph = null; source = null; } // *** Some basic methods *** /** * Dijkstra's algorithm computes shortest paths from a given source node to * all nodes in a graph. This method returns the source node. * * @return the source node * @see #setSource(Node) */ @SuppressWarnings("unchecked") public T getSource() { return (T) source; } /** * Dijkstra's algorithm computes shortest paths from a given source node to * all nodes in a graph. This method sets the source node. * * @param source * The new source node. * @see #getSource() */ public void setSource(Node source) { this.source = source; } /** * Removes the attributes used to store internal solution data in the nodes * of the graph. Use this method to free memory. Solution access methods * must not be used after calling this method. */ @Override public void clear() { super.clear(); for (Node node : graph) { Data data = node.getAttribute(resultAttribute); if (data != null) { data.fn = null; data.edgeFromParent = null; } node.removeAttribute(resultAttribute); } } // *** Methods of Algorithm interface *** /** * Computes the shortest paths from the source node to all nodes in the * graph. * * @throws IllegalStateException * if {@link #init(Graph)} or {@link #setSource(Node)} have not * been called before or if elements with negative lengths are * discovered. * @see org.graphstream.algorithm.Algorithm#compute() * @complexity O(m + nlogn) where m is * the number of edges and n is the number of nodes in * the graph. */ @Override public void compute() { // check if computation can start if (graph == null) throw new IllegalStateException( "No graph specified. Call init() first."); if (source == null) throw new IllegalStateException( "No source specified. Call setSource() first."); resetFlags(); makeTree(); } @Override protected void makeTree() { // initialization FibonacciHeap heap = new FibonacciHeap(); for (Node node : graph) { Data data = new Data(); double v = node == source ? getSourceLength() : Double.POSITIVE_INFINITY; data.fn = heap.add(v, node); data.edgeFromParent = null; node.addAttribute(resultAttribute, data); } // main loop while (!heap.isEmpty()) { Node u = heap.extractMin(); Data dataU = u.getAttribute(resultAttribute); dataU.distance = dataU.fn.getKey(); dataU.fn = null; if (dataU.edgeFromParent != null) edgeOn(dataU.edgeFromParent); for (Edge e : u.getEachLeavingEdge()) { Node v = e.getOpposite(u); Data dataV = v.getAttribute(resultAttribute); if (dataV.fn == null) continue; double tryDist = dataU.distance + getLength(e, v); if (tryDist < dataV.fn.getKey()) { dataV.edgeFromParent = e; heap.decreaseKey(dataV.fn, tryDist); } } } } // *** Iterators *** protected class NodeIterator implements Iterator { protected Node nextNode; protected NodeIterator(Node target) { nextNode = Double.isInfinite(getPathLength(target)) ? null : target; } public boolean hasNext() { return nextNode != null; } @SuppressWarnings("unchecked") public T next() { if (nextNode == null) throw new NoSuchElementException(); Node node = nextNode; nextNode = getParent(nextNode); return (T) node; } public void remove() { throw new UnsupportedOperationException( "remove is not supported by this iterator"); } } protected class EdgeIterator implements Iterator { protected Node nextNode; protected T nextEdge; protected EdgeIterator(Node target) { nextNode = target; nextEdge = getEdgeFromParent(nextNode); } public boolean hasNext() { return nextEdge != null; } public T next() { if (nextEdge == null) throw new NoSuchElementException(); T edge = nextEdge; nextNode = getParent(nextNode); nextEdge = getEdgeFromParent(nextNode); return edge; } public void remove() { throw new UnsupportedOperationException( "remove is not supported by this iterator"); } } protected class PathIterator implements Iterator { protected List nodes; protected List> iterators; protected Path nextPath; protected void extendPathStep() { int last = nodes.size() - 1; Node v = nodes.get(last); double lengthV = getPathLength(v); Iterator it = iterators.get(last); while (it.hasNext()) { Edge e = it.next(); Node u = e.getOpposite(v); if (getPathLength(u) + getLength(e, v) == lengthV) { nodes.add(u); iterators.add(u.getEnteringEdgeIterator()); return; } } nodes.remove(last); iterators.remove(last); } protected void extendPath() { while (!nodes.isEmpty() && nodes.get(nodes.size() - 1) != source) extendPathStep(); } protected void constructNextPath() { if (nodes.isEmpty()) { nextPath = null; return; } nextPath = new Path(); nextPath.setRoot(source); for (int i = nodes.size() - 1; i > 0; i--) nextPath.add(nodes.get(i).getEdgeToward( nodes.get(i - 1).getId())); } public PathIterator(Node target) { nodes = new ArrayList(); iterators = new ArrayList>(); if (Double.isInfinite(getPathLength(target))) { nextPath = null; return; } nodes.add(target); iterators.add(target.getEnteringEdgeIterator()); extendPath(); constructNextPath(); } public boolean hasNext() { return nextPath != null; } public Path next() { if (nextPath == null) throw new NoSuchElementException(); nodes.remove(nodes.size() - 1); iterators.remove(iterators.size() - 1); extendPath(); Path path = nextPath; constructNextPath(); return path; } public void remove() { throw new UnsupportedOperationException( "remove is not supported by this iterator"); } } protected class TreeIterator implements Iterator { Iterator nodeIt; T nextEdge; protected void findNextEdge() { nextEdge = null; while (nodeIt.hasNext() && nextEdge == null) nextEdge = getEdgeFromParent(nodeIt.next()); } protected TreeIterator() { nodeIt = graph.getNodeIterator(); findNextEdge(); } public boolean hasNext() { return nextEdge != null; } public T next() { if (nextEdge == null) throw new NoSuchElementException(); T edge = nextEdge; findNextEdge(); return edge; } public void remove() { throw new UnsupportedOperationException( "remove is not supported by this iterator"); } } // *** Methods to access the solution *** /** * Returns the length of the shortest path from the source node to a given * target node. * * @param target * A node * @return the length of the shortest path or * {@link java.lang.Double#POSITIVE_INFINITY} if there is no path * from the source to the target * @complexity O(1) */ public double getPathLength(Node target) { return target. getAttribute(resultAttribute).distance; } /** * Dijkstra's algorithm produces a shortest path tree rooted in the source * node. This method returns the total length of the tree. * * @return the length of the shortest path tree * @complexity O(n) where n is the number of nodes is the * graph. */ public double getTreeLength() { double length = getSourceLength(); for (Edge edge : getTreeEdges()) { Node node = edge.getNode0(); if (getEdgeFromParent(node) != edge) node = edge.getNode1(); length += getLength(edge, node); } return length; } /** * Returns the edge between the target node and the previous node in the * shortest path from the source to the target. This is also the edge * connecting the target to its parent in the shortest path tree. * * @param target * a node * @return the edge between the target and its predecessor in the shortest * path, {@code null} if there is no path from the source to the * target or if the target and the source are the same node. * @see #getParent(Node) * @complexity O(1) */ @SuppressWarnings("unchecked") public T getEdgeFromParent(Node target) { return (T) target. getAttribute(resultAttribute).edgeFromParent; } /** * Returns the node preceding the target in the shortest path from the * source to the target. This node is the parent of the target in the * shortest path tree. * * @param target * a node * @return the predecessor of the target in the shortest path, {@code null} * if there is no path from the source to the target or if the * target and the source are the same node. * @see #getEdgeFromParent(Node) * @complexity O(1) */ public T getParent(Node target) { Edge edge = getEdgeFromParent(target); if (edge == null) return null; return edge.getOpposite(target); } /** * This iterator traverses the nodes on the shortest path from the source * node to a given target node. The nodes are traversed in reverse order: * the target node first, then its predecessor, ... and finally the source * node. If there is no path from the source to the target, no nodes are * traversed. This iterator does not support * {@link java.util.Iterator#remove()}. * * @param target * a node * @return an iterator on the nodes of the shortest path from the source to * the target * @see #getPathNodes(Node) * @complexity Each call of {@link java.util.Iterator#next()} of this * iterator takes O(1) time */ public Iterator getPathNodesIterator(Node target) { return new NodeIterator(target); } /** * An iterable view of the nodes on the shortest path from the source node * to a given target node. Uses {@link #getPathNodesIterator(Node)}. * * @param target * a node * @return an iterable view of the nodes on the shortest path from the * source to the target * @see #getPathNodesIterator(Node) */ public Iterable getPathNodes(final Node target) { return new Iterable() { public Iterator iterator() { return getPathNodesIterator(target); } }; } /** * This iterator traverses the edges on the shortest path from the source * node to a given target node. The edges are traversed in reverse order: * first the edge between the target and its predecessor, ... and finally * the edge between the source end its successor. If there is no path from * the source to the target or if he source and the target are the same * node, no edges are traversed. This iterator does not support * {@link java.util.Iterator#remove()}. * * @param target * a node * @return an iterator on the edges of the shortest path from the source to * the target * @see #getPathEdges(Node) * @complexity Each call of {@link java.util.Iterator#next()} of this * iterator takes O(1) time */ public Iterator getPathEdgesIterator(Node target) { return new EdgeIterator(target); } /** * An iterable view of the edges on the shortest path from the source node * to a given target node. Uses {@link #getPathEdgesIterator(Node)}. * * @param target * a node * @return an iterable view of the edges on the shortest path from the * source to the target * @see #getPathEdgesIterator(Node) */ public Iterable getPathEdges(final Node target) { return new Iterable() { public Iterator iterator() { return getPathEdgesIterator(target); } }; } /** * This iterator traverses all the shortest paths from the source * node to a given target node. If there is more than one shortest paths * between the source and the target, other solution access methods choose * one of them (the one from the shortest path tree). This iterator can be * used if one needs to know all the paths. Each call to * {@link java.util.Iterator#next()} method of this iterator returns a * shortest path in the form of {@link org.graphstream.graph.Path} object. * This iterator does not support {@link java.util.Iterator#remove()}. * * @param target * a node * @return an iterator on all the shortest paths from the source to the * target * @see #getAllPaths(Node) * @complexity Each call of {@link java.util.Iterator#next()} of this * iterator takes O(m) time in the worst case, where * m is the number of edges in the graph */ public Iterator getAllPathsIterator(Node target) { return new PathIterator(target); } /** * An iterable view of of all the shortest paths from the source * node to a given target node. Uses {@link #getAllPathsIterator(Node)} * * @param target * a node * @return an iterable view of all the shortest paths from the source to the * target * @see #getAllPathsIterator(Node) */ public Iterable getAllPaths(final Node target) { return new Iterable() { public Iterator iterator() { return getAllPathsIterator(target); } }; } /** * Dijkstra's algorithm produces a shortest path tree rooted in the source * node. This iterator traverses the edges of this tree. The edges are * traversed in no particular order. * * @return an iterator on the edges of the shortest path tree * @see #getTreeEdges() * @complexity Each call of {@link java.util.Iterator#next()} of this * iterator takes O(1) time */ @Override public Iterator getTreeEdgesIterator() { return new TreeIterator(); } /** * Returns the shortest path from the source node to a given target node. If * there is no path from the source to the target returns an empty path. * This method constructs a {@link org.graphstream.graph.Path} object which * consumes heap memory proportional to the number of edges and nodes in the * path. When possible, prefer using {@link #getPathNodes(Node)} and * {@link #getPathEdges(Node)} which are more memory- and time-efficient. * * @param target * a node * @return the shortest path from the source to the target * @complexity O(p) where p is the number of the nodes in * the path */ public Path getPath(Node target) { Path path = new Path(); if (Double.isInfinite(getPathLength(target))) return path; Stack stack = new Stack(); for (Edge e : getPathEdges(target)) stack.push(e); path.setRoot(source); while (!stack.isEmpty()) path.add(stack.pop()); return path; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy