Java tutorial
/* // Licensed to Julian Hyde under one or more contributor license // agreements. See the NOTICE file distributed with this work for // additional information regarding copyright ownership. // // Julian Hyde licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at: // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ package net.hydromatic.optiq.util.graph; import org.eigenbase.util.Pair; import com.google.common.collect.ImmutableList; import java.util.*; /** * Miscellaneous graph utilities. */ public class Graphs { private Graphs() { } public static <V, E extends DefaultEdge> List<V> predecessorListOf(DirectedGraph<V, E> graph, V vertex) { final List<E> edges = graph.getInwardEdges(vertex); return new AbstractList<V>() { public V get(int index) { //noinspection unchecked return (V) edges.get(index).source; } public int size() { return edges.size(); } }; } /** Returns a map of the shortest paths between any pair of nodes. */ public static <V, E extends DefaultEdge> FrozenGraph<V, E> makeImmutable(DirectedGraph<V, E> graph) { DefaultDirectedGraph<V, E> graph1 = (DefaultDirectedGraph<V, E>) graph; Map<Pair<V, V>, List<V>> shortestPaths = new HashMap<Pair<V, V>, List<V>>(); for (DefaultDirectedGraph.VertexInfo<V, E> arc : graph1.vertexMap.values()) { for (E edge : arc.outEdges) { final V source = graph1.source(edge); final V target = graph1.target(edge); shortestPaths.put(Pair.of(source, target), ImmutableList.of(source, target)); } } while (true) { // Take a copy of the map's keys to avoid // ConcurrentModificationExceptions. final List<Pair<V, V>> previous = ImmutableList.copyOf(shortestPaths.keySet()); int changeCount = 0; for (E edge : graph.edgeSet()) { for (Pair<V, V> edge2 : previous) { if (edge.target.equals(edge2.left)) { final Pair<V, V> key = Pair.of(graph1.source(edge), edge2.right); List<V> bestPath = shortestPaths.get(key); List<V> arc2Path = shortestPaths.get(edge2); if ((bestPath == null) || (bestPath.size() > (arc2Path.size() + 1))) { ImmutableList.Builder<V> newPath = ImmutableList.builder(); newPath.add(graph1.source(edge)); newPath.addAll(arc2Path); shortestPaths.put(key, newPath.build()); changeCount++; } } } } if (changeCount == 0) { break; } } return new FrozenGraph<V, E>(graph1, shortestPaths); } /** * Immutable grap. * * @param <V> Vertex type * @param <E> Edge type */ public static class FrozenGraph<V, E extends DefaultEdge> { private final DefaultDirectedGraph<V, E> graph; private final Map<Pair<V, V>, List<V>> shortestPaths; /** Creates a frozen graph as a copy of another graph. */ FrozenGraph(DefaultDirectedGraph<V, E> graph, Map<Pair<V, V>, List<V>> shortestPaths) { this.graph = graph; this.shortestPaths = shortestPaths; } /** * Returns an iterator of all paths between two nodes, shortest first. * * <p>The current implementation is not optimal.</p> */ public List<List<V>> getPaths(V from, V to) { List<List<V>> list = new ArrayList<List<V>>(); findPaths(from, to, list); return list; } /** * Returns the shortest path between two points, null if there is no path. * * @param from From * @param to To * * @return A list of arcs, null if there is no path. */ public List<V> getShortestPath(V from, V to) { if (from.equals(to)) { return ImmutableList.of(); } return shortestPaths.get(Pair.of(from, to)); } private void findPaths(V from, V to, List<List<V>> list) { final List<V> shortestPath = shortestPaths.get(Pair.of(from, to)); if (shortestPath == null) { return; } // final E edge = graph.getEdge(from, to); // if (edge != null) { // list.add(ImmutableList.of(from, to)); // } final List<V> prefix = new ArrayList<V>(); prefix.add(from); findPathsExcluding(from, to, list, new HashSet<V>(), prefix); } /** * Finds all paths from "from" to "to" of length 2 or greater, such that the * intermediate nodes are not contained in "excludedNodes". */ private void findPathsExcluding(V from, V to, List<List<V>> list, Set<V> excludedNodes, List<V> prefix) { excludedNodes.add(from); for (E edge : graph.edges) { if (edge.source.equals(from)) { final V target = graph.target(edge); if (target.equals(to)) { // We found a path. prefix.add(target); list.add(ImmutableList.copyOf(prefix)); prefix.remove(prefix.size() - 1); } else if (excludedNodes.contains(target)) { // ignore it } else { prefix.add(target); findPathsExcluding(target, to, list, excludedNodes, prefix); prefix.remove(prefix.size() - 1); } } } excludedNodes.remove(from); } } } // End Graphs.java