programming-examples/java/Graph_Problems_Algorithms/PushRelabel.java

252 lines
7.7 KiB
Java
Raw Normal View History

2019-11-15 12:59:38 +01:00
package com.jwetherell.algorithms.graph;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;
import com.jwetherell.algorithms.data_structures.Graph;
/**
* In mathematical optimization, the pushrelabel algorithm (alternatively, preflowpush
* algorithm) is an algorithm for computing maximum flows. The name "pushrelabel" comes
* from the two basic operations used in the algorithm. Throughout its execution, the
* algorithm maintains a "preflow" and gradually converts it into a maximum flow by moving
* flow locally between neighboring nodes using push operations under the guidance of an
* admissible network maintained by relabel operations.
*
* https://en.wikipedia.org/wiki/Push%E2%80%93relabel_maximum_flow_algorithm
*
* @author Miron Ficak <miron.ficak@gmail.com>
*/
public class PushRelabel {
private final Queue<Vertex> queue = new ArrayDeque<Vertex>();
private final List<Vertex> vertices = new ArrayList<Vertex>();
private int relabelCounter;
private int n;
private Vertex source;
private Vertex sink;
private PushRelabel(Collection<Vertex> vertices, Vertex source, Vertex sink) {
this.vertices.addAll(vertices);
this.source = source;
this.sink = sink;
this.n = vertices.size();
}
/**
* Computes maximum flow in flow network, using push-relabel algorithm with O(V^3) complexity.
*
* @param edgesToCapacities represents edges of network with capacities
* @param source source of network
* @param sink sink of network
* @param <T> parameter of graph on which network is based
* @return the maximum flow
*/
public static <T extends Comparable<T>> Long getMaximumFlow(Map<Graph.Edge<T>, Long> edgesToCapacities, Graph.Vertex<T> source, Graph.Vertex<T> sink) {
if (edgesToCapacities == null)
throw new IllegalArgumentException("Graph is NULL.");
final Map<Graph.Vertex<T>, Vertex> vertexMap = new TreeMap<Graph.Vertex<T>, Vertex>();
for (Graph.Edge<T> edge : edgesToCapacities.keySet()) {
vertexMap.put(edge.getFromVertex(), new Vertex());
vertexMap.put(edge.getToVertex(), new Vertex());
}
final Vertex s = new Vertex(); // source
vertexMap.put(source, s);
final Vertex t = new Vertex(); // sink
vertexMap.put(sink, t);
final PushRelabel pushRelabel = new PushRelabel(vertexMap.values(), s, t);
for (Map.Entry<Graph.Edge<T>, Long> edgeWithCapacity : edgesToCapacities.entrySet()) {
final Graph.Edge<T> e = edgeWithCapacity.getKey();
addEdge(
vertexMap.get(e.getFromVertex()),
vertexMap.get(e.getToVertex()),
edgeWithCapacity.getValue()
);
}
return pushRelabel.maxFlow();
}
private static final void addEdge(Vertex from, Vertex to, long cost) {
final int placeOfEdge = from.edges.indexOf(new Edge(from, to));
if (placeOfEdge == -1) {
final Edge edge = new Edge(from, to, cost);
final Edge revertedEdge = new Edge(to, from, 0);
edge.revertedEdge = revertedEdge;
revertedEdge.revertedEdge = edge;
from.edges.add(edge);
to.edges.add(revertedEdge);
} else {
from.edges.get(placeOfEdge).cost += cost;
}
}
private final void recomputeHeight() {
final Queue<Vertex> que = new ArrayDeque<Vertex>();
for (Vertex vertex : vertices) {
vertex.visited = false;
vertex.height = 2 * n;
}
sink.height = 0;
source.height = n;
source.visited = true;
sink.visited = true;
que.add(sink);
while (!que.isEmpty()) {
final Vertex act = que.poll();
for (Edge e : act.edges) {
if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) {
e.to.height = act.height + 1;
que.add(e.to);
e.to.visited = true;
}
}
}
que.add(source);
while (!que.isEmpty()) {
final Vertex act = que.poll();
for (Edge e : act.edges) {
if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) {
e.to.height = act.height + 1;
que.add(e.to);
e.to.visited = true;
}
}
}
}
private final void init() {
for (Edge e : source.edges) {
e.flow = e.cost;
e.revertedEdge.flow = -e.flow;
e.to.excess += e.flow;
if (e.to != source && e.to != sink)
queue.add(e.to);
}
recomputeHeight();
relabelCounter = 0;
}
private static final void relabel(Vertex v) {
int minimum = 0;
for (Edge e : v.edges) {
if (e.flow < e.cost)
minimum = Math.min(minimum, e.to.height);
}
v.height = minimum + 1;
}
private final void push(Vertex u, Edge e) {
final long delta = (u.excess < e.cost - e.flow) ? u.excess : e.cost - e.flow;
e.flow += delta;
e.revertedEdge.flow -= delta;
u.excess -= delta;
if (e.to.excess == 0 && e.to != source && e.to != sink)
queue.add(e.to);
e.to.excess += delta;
}
private final void discharge(Vertex u) {
while (u.excess > 0) {
if (u.currentEdge == u.edges.size()) {
relabel(u);
if ((++relabelCounter) == n) {
recomputeHeight();
for (Vertex vertex : vertices)
vertex.currentEdge = 0;
relabelCounter = 0;
}
u.currentEdge = 0;
} else {
Edge e = u.edges.get(u.currentEdge);
if (e.flow < e.cost && u.height == e.to.height + 1)
push(u, e);
else
u.currentEdge++;
}
}
}
private final long maxFlow() {
init();
while (!queue.isEmpty())
discharge(queue.poll());
return sink.excess;
}
private static final class Vertex {
private final List<Edge> edges = new ArrayList<Edge>();
private boolean visited = false;
private int height;
private int currentEdge;
private long excess;
}
private final static class Edge {
private final Vertex from;
private final Vertex to;
private long cost;
private long flow;
private Edge revertedEdge;
private Edge(Vertex from, Vertex to, long cost) {
this.from = from;
this.to = to;
this.cost = cost;
}
private Edge(Vertex from, Vertex to) {
this.from = from;
this.to = to;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final Edge edge = (Edge) o;
if (!from.equals(edge.from))
return false;
return to.equals(edge.to);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int result = from.hashCode();
result = 31 * result + to.hashCode();
return result;
}
}
}