Skip to content

Commit cb1ee2e

Browse files
committed
code
1 parent 384035f commit cb1ee2e

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed

BipartiteMatching.java

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import java.util.*;
2+
3+
public class BipartiteMatching {
4+
5+
class Edge{
6+
private static final int defaultEdgeCapacity = 1; //This Flow network is for bipartite matching so the default capacity is always 1
7+
private int fromVertex; //an edge is composed of 2 vertices
8+
private int toVertex;
9+
private int capacity; //edges also have a capacity & a flow
10+
private int flow;
11+
12+
//Overloaded constructor to create a generic edge with a default capacity
13+
public Edge(int fromVertex, int toVertex){
14+
this(fromVertex, toVertex, defaultEdgeCapacity);
15+
}
16+
17+
public Edge(int fromVertex, int toVertex, int capacity){
18+
this.fromVertex = fromVertex;
19+
this.toVertex = toVertex;
20+
this.capacity = capacity;
21+
}
22+
23+
//Given an end-node, Returns the other end-node (completes the edge)
24+
public int getOtherEndNode(int vertex){
25+
if(vertex==fromVertex){
26+
return toVertex;
27+
}
28+
return fromVertex;
29+
}
30+
31+
public int getCapacity(){
32+
return capacity;
33+
}
34+
35+
public int getFlow(){
36+
return flow;
37+
}
38+
39+
public int residualCapacityTo(int vertex){
40+
if(vertex==fromVertex){
41+
return flow;
42+
}
43+
return (capacity-flow);
44+
}
45+
46+
public void increaseFlowTo(int vertex, int changeInFlow){
47+
if(vertex==fromVertex){
48+
flow = flow-changeInFlow;
49+
}
50+
else{
51+
flow = flow+changeInFlow;
52+
}
53+
}
54+
55+
//Prints edge using Array indexes, not human readable ID's like "S" or "T"
56+
@Override
57+
public String toString(){
58+
return "(" + fromVertex+" --> "+toVertex + ")";
59+
}
60+
}
61+
62+
63+
64+
private ArrayList<ArrayList<Edge>> graph; //Graph is represented as an ArrayList of Edges
65+
private ArrayList<String> getStringVertexIdFromArrayIndex; //convert between array indexes (starting from 0) & human readable vertex names
66+
private int vertexCount; //How many vertices are in the graph
67+
68+
//These fields are updated by fordFulkersonMaxFlow and when finding augmentation paths
69+
private Edge[] edgeTo;
70+
private boolean[] isVertexMarked; //array of all vertices, updated each time an augmentation path is found
71+
private int flow;
72+
73+
//Constructor initializes graph edge list with number of vertexes, string equivalents for array indexes & adds empty ArrayLists to the graph for how many vertices ther are
74+
public BipartiteMatching(int vertexCount, ArrayList<String> getStringVertexIdFromArrayIndex){
75+
this.vertexCount = vertexCount;
76+
this.getStringVertexIdFromArrayIndex = getStringVertexIdFromArrayIndex;
77+
78+
graph = new ArrayList<>(vertexCount); //Populate graph with empty ArrayLists for each vertex
79+
for(int i=0; i<vertexCount; ++i){
80+
graph.add(new ArrayList<>());
81+
}
82+
}
83+
84+
public void addEdge(int fromVertex, int toVertex){
85+
Edge newEdge = new Edge(fromVertex, toVertex); //create new edge between 2 vertices
86+
graph.get(fromVertex).add(newEdge); //Undirected bipartie graph, so add edge in both directions
87+
graph.get(toVertex).add(newEdge);
88+
}
89+
90+
//Adds edges from the source to all vertices in the left half
91+
public void connectSourceToLeftHalf(int source, int[] leftHalfVertices){
92+
for(int vertexIndex : leftHalfVertices){
93+
// System.out.println("addEdge(source, vertexIndex) = ("+source+", "+vertexIndex+")");
94+
this.addEdge(source, vertexIndex);
95+
}
96+
}
97+
98+
//Adds edges from all vertices in right half to sink
99+
public void connectSinkToRightHalf(int sink, int[] rightHalfVertices){
100+
for(int vertexIndex : rightHalfVertices){
101+
// System.out.println("addEdge(vertexIndex, sink) = ("+vertexIndex+", "+sink+")");
102+
this.addEdge(vertexIndex, sink);
103+
}
104+
}
105+
106+
//Finds max flow / min cut of a graph
107+
public void fordFulkersonMaxFlow(int source, int sink){
108+
edgeTo = new Edge[vertexCount];
109+
while(existsAugmentingPath(source, sink)){
110+
int flowIncrease = 1; //default value is 1 since it's a bipartite matching problem with capacities = 1
111+
112+
System.out.print("Matched Vertices "+(getStringVertexIdFromArrayIndex.get( edgeTo[sink].getOtherEndNode(sink) ) )+" & "); //Print the 1st vertex of the pair
113+
//Loop over The path from source to sink. (Update max flow & print the other matched vertex)
114+
for(int i=sink; i!=source; i=edgeTo[i].getOtherEndNode(i)){
115+
//Loop stops when i reaches the source, so print out the vertex in the path that comes right before the source
116+
if(edgeTo[i].getOtherEndNode(i)==source){
117+
System.out.println(getStringVertexIdFromArrayIndex.get(i)); //use human readable vertex ID's
118+
}
119+
flowIncrease = Math.min(flowIncrease, edgeTo[i].residualCapacityTo(i));
120+
}
121+
122+
//Update Residual Capacities
123+
for(int i=sink; i!=source; i=edgeTo[i].getOtherEndNode(i)){
124+
edgeTo[i].increaseFlowTo(i, flowIncrease);
125+
}
126+
flow+=flowIncrease;
127+
}
128+
System.out.println("\nMaximum pairs matched = "+flow);
129+
}
130+
131+
//Calls dfs to find an augmentation path & check if it reached the sink
132+
public boolean existsAugmentingPath(int source, int sink){
133+
isVertexMarked = new boolean[vertexCount]; //recreate array of visited nodes each time searching for a path
134+
isVertexMarked[source] = true; //visit the source
135+
136+
// System.out.print("Augmenting Path : S ");
137+
depthFirstSearch(source, sink); //attempts to find path from source to sink & updates isVertexMarked
138+
// System.out.print("T ");
139+
140+
return isVertexMarked[sink]; //if it reached the sink, then a path was found
141+
}
142+
143+
public void depthFirstSearch(int v, int sink){
144+
if(v==sink){ //No point in finding a path if the starting vertex is already at the sink
145+
return;
146+
}
147+
148+
for(Edge edge : graph.get(v)){ //loop over all edges in the graph
149+
int otherEndNode = edge.getOtherEndNode(v);
150+
if(!isVertexMarked[otherEndNode] && edge.residualCapacityTo(otherEndNode)>0 ){ //if otherEndNode is unvisited AND if the residual capacity exists at the otherEndNode
151+
// System.out.print( getStringVertexIdFromArrayIndex.get(otherEndNode) +" ");
152+
edgeTo[otherEndNode] = edge; //update next link in edge chain
153+
isVertexMarked[otherEndNode] = true; //visit the node
154+
depthFirstSearch(otherEndNode, sink); //recursively continue exploring
155+
}
156+
}
157+
}
158+
159+
160+
public static void main(String[] args){
161+
int vertexCount = 10;
162+
int vertexCountIncludingSourceAndSink = vertexCount +2;
163+
//convert between array indexes (starting from 0) & human readable vertex names
164+
ArrayList<String> getStringVertexIdFromArrayIndex = new ArrayList<String>(Arrays.asList("1", "2", "3", "4", "5", "1'", "2'", "3'", "4'", "5'"));
165+
getStringVertexIdFromArrayIndex.add("S"); //Add source & sink as last 2 items in the list
166+
getStringVertexIdFromArrayIndex.add("T");
167+
168+
int source = vertexCount; //source & sink as array indexes
169+
int sink = vertexCount+1;
170+
171+
//These must be consecutive indexes. rightHalfVertices starts with the next integer after the last item in leftHaldVertices
172+
int[] leftHalfVertices ={0, 1, 2, 3, 4}; //source is connected to these vertices
173+
int[] rightHalfVertices = {5, 6, 7, 8, 9}; //sink is connected to these vertices
174+
175+
BipartiteMatching graph1BipartiteMatcher = new BipartiteMatching(vertexCountIncludingSourceAndSink, getStringVertexIdFromArrayIndex);
176+
graph1BipartiteMatcher.addEdge(0, 5);
177+
graph1BipartiteMatcher.addEdge(0, 6);
178+
graph1BipartiteMatcher.addEdge(1, 6);
179+
graph1BipartiteMatcher.addEdge(2, 5);
180+
graph1BipartiteMatcher.addEdge(2, 7);
181+
graph1BipartiteMatcher.addEdge(2, 8);
182+
graph1BipartiteMatcher.addEdge(3, 6);
183+
graph1BipartiteMatcher.addEdge(3, 9);
184+
graph1BipartiteMatcher.addEdge(4, 6);
185+
graph1BipartiteMatcher.addEdge(4, 9);
186+
187+
graph1BipartiteMatcher.connectSourceToLeftHalf(source, leftHalfVertices);
188+
graph1BipartiteMatcher.connectSinkToRightHalf(sink, rightHalfVertices);
189+
190+
System.out.println("Running Bipartite Matching on Graph 1");
191+
graph1BipartiteMatcher.fordFulkersonMaxFlow(source, sink);
192+
System.out.println("\n");
193+
194+
195+
//Graph 2
196+
int vertexCount2 = 11;
197+
int vertexCountIncludingSourceAndSink2 = vertexCount2 +2;
198+
//convert between array indexes (starting from 0) & human readable vetex names
199+
ArrayList<String> getStringVertexIdFromArrayIndex2 = new ArrayList<String>(Arrays.asList("1", "2", "3", "4", "5", "6", "1'", "2'", "3'", "4'", "5'"));
200+
getStringVertexIdFromArrayIndex2.add("S"); //Add source & sink as last 2 items in the list
201+
getStringVertexIdFromArrayIndex2.add("T");
202+
203+
int source2 = vertexCount2; //add a source & sink (these are array indexes)
204+
int sink2 = vertexCount2+1;
205+
206+
//these must be consecutive indexes. rightHalfVertices starts with the next integer after the last item in leftHaldVertices
207+
int[] leftHalfVertices2 ={0, 1, 2, 3, 4, 5}; //source is connected to these vertices
208+
int[] rightHalfVertices2 = {6, 7, 8, 9, 10}; //sink is connected to these vertices
209+
210+
BipartiteMatching graph2BipartiteMatcher = new BipartiteMatching(vertexCountIncludingSourceAndSink2, getStringVertexIdFromArrayIndex2);
211+
graph2BipartiteMatcher.addEdge(0, 7);
212+
graph2BipartiteMatcher.addEdge(1, 6);
213+
graph2BipartiteMatcher.addEdge(2, 7);
214+
graph2BipartiteMatcher.addEdge(3, 6);
215+
graph2BipartiteMatcher.addEdge(3, 8);
216+
graph2BipartiteMatcher.addEdge(3, 9);
217+
graph2BipartiteMatcher.addEdge(4, 7);
218+
graph2BipartiteMatcher.addEdge(4, 9);
219+
graph2BipartiteMatcher.addEdge(5, 8);
220+
graph2BipartiteMatcher.addEdge(5, 10);
221+
222+
graph2BipartiteMatcher.connectSourceToLeftHalf(source2, leftHalfVertices2);
223+
graph2BipartiteMatcher.connectSinkToRightHalf(sink2, rightHalfVertices2);
224+
225+
System.out.println("Running Bipartite Matching on Graph 2");
226+
graph2BipartiteMatcher.fordFulkersonMaxFlow(source2, sink2);
227+
}
228+
}

0 commit comments

Comments
 (0)