programming-examples/java/Graph_Problems_Algorithms/PushRelabel.java
2019-11-15 12:59:38 +01:00

252 lines
7.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}