Skip to content

Commit c00f23f

Browse files
authored
Merge pull request #60 from Debashis08/feature-graph-implementation
Feature graph implementation
2 parents 21c277d + 29ef6ce commit c00f23f

File tree

5 files changed

+341
-0
lines changed

5 files changed

+341
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#pragma once
2+
3+
#include<map>
4+
#include<vector>
5+
using namespace std;
6+
7+
namespace MaximumBipartiteMatching
8+
{
9+
enum Color
10+
{
11+
WHITE = -1,
12+
RED = 0,
13+
BLUE = 1
14+
};
15+
16+
class Graph
17+
{
18+
private:
19+
int _noOfVertices;
20+
int _source;
21+
int _sink;
22+
int _maximumFlow;
23+
bool _flagParallelEdges;
24+
bool _isBipartite;
25+
vector<vector<int>> _adjMatrix;
26+
vector<vector<int>> _residualGraph;
27+
vector<int> _parent;
28+
vector<bool> _visited;
29+
vector<int> _color;
30+
vector<vector<int>> _matchings;
31+
void ResolveAntiParallelEdges();
32+
void ColorGraph();
33+
void AddAdditionalEdges();
34+
bool BreadthFirstSearch();
35+
public:
36+
void CreateGraph(int noOfVertices);
37+
void PushDirectedEdge(int valueU, int valueV);
38+
int FindMaximumBipartiteMatching();
39+
vector<vector<int>> GetMatchings();
40+
};
41+
}
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
#include "../Headers/0003_Graph/0017_MaximumBipartiteMatching.h"
2+
#include<climits>
3+
#include<queue>
4+
using namespace std;
5+
6+
namespace MaximumBipartiteMatching
7+
{
8+
// Graph Private Member Methods
9+
void Graph::ResolveAntiParallelEdges()
10+
{
11+
int countParallelEdges = 0;
12+
for (int i = 0; i < this->_noOfVertices; i++)
13+
{
14+
for (int j = 0; j < this->_noOfVertices; j++)
15+
{
16+
if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0)
17+
{
18+
countParallelEdges++;
19+
}
20+
}
21+
}
22+
23+
// As i->j and j->i both edges has been counted, actual count is count = count / 2
24+
countParallelEdges /= 2;
25+
26+
this->_flagParallelEdges = countParallelEdges > 0;
27+
28+
// If there are no anti-parallel edges, no need to modify the adjMatrix
29+
if (!this->_flagParallelEdges)
30+
{
31+
return;
32+
}
33+
34+
int newNoOfVertices = this->_noOfVertices + countParallelEdges;
35+
36+
// Modifying the adjMatrix
37+
for (auto& edge : this->_adjMatrix)
38+
{
39+
edge.resize(newNoOfVertices, 0);
40+
}
41+
int k = this->_noOfVertices;
42+
this->_visited.resize(newNoOfVertices, false);
43+
this->_parent.resize(newNoOfVertices, -1);
44+
this->_adjMatrix.resize(newNoOfVertices, vector<int>(newNoOfVertices, 0));
45+
46+
// Removing the anti-parallel edges by adding new nodes
47+
for (int i = 0; i < this->_noOfVertices; i++)
48+
{
49+
for (int j = 0; j < this->_noOfVertices; j++)
50+
{
51+
if (this->_adjMatrix[i][j] > 0 && this->_adjMatrix[j][i] > 0)
52+
{
53+
this->_adjMatrix[i][k] = this->_adjMatrix[i][j];
54+
this->_adjMatrix[k][j] = this->_adjMatrix[i][j];
55+
this->_adjMatrix[i][j] = 0;
56+
k++;
57+
}
58+
}
59+
}
60+
61+
// Updating the total no of vertices after modifying the adjMatrix
62+
this->_noOfVertices = newNoOfVertices;
63+
}
64+
65+
// This method is used to color the vertices of the graph to determine if the given graph is bipartite or not
66+
void Graph::ColorGraph()
67+
{
68+
// Color of all the vertices are initialised to WHITE
69+
fill(this->_color.begin(), this->_color.end(), WHITE);
70+
71+
// Queue to hold the vertices
72+
queue<int> nodeQueue;
73+
74+
for (int node = 0; node < this->_noOfVertices; node++)
75+
{
76+
// Check if the node is already not colored
77+
if (this->_color[node] == WHITE)
78+
{
79+
// The color of the node is set to RED
80+
this->_color[node] = RED;
81+
82+
// The node is inserted into the queue
83+
nodeQueue.push(node);
84+
85+
// Using BFS method to color all the vertices
86+
while (!nodeQueue.empty())
87+
{
88+
int nodeU = nodeQueue.front();
89+
nodeQueue.pop();
90+
91+
// Iterating over G.Adj[nodeU]
92+
for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++)
93+
{
94+
// As there are no self loops, continue
95+
if (nodeU == nodeV)
96+
{
97+
continue;
98+
}
99+
// Check if there is an edge u --> v and nodeV is not colored yet
100+
else if (this->_residualGraph[nodeU][nodeV] != 0 && this->_color[nodeV] == WHITE)
101+
{
102+
// Set the color of nodeV opposite of nodeU
103+
this->_color[nodeV] = 1 - this->_color[nodeU];
104+
// Insert the nodeV into the queue
105+
nodeQueue.push(nodeV);
106+
}
107+
// Check if there is an edge u --> v and nodeV is of same color as nodeU
108+
else if (this->_residualGraph[nodeU][nodeV] != 0 && this->_color[nodeV] == this->_color[nodeU])
109+
{
110+
// Set the _isBipartite flag to false and return
111+
this->_isBipartite = false;
112+
return;
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
// If the above operation completes without returning yet that indicates the graph is bipartite
120+
// Set the _isBipartite flag to true and return
121+
this->_isBipartite = true;
122+
return;
123+
}
124+
125+
// This method is used to create the additional edges
126+
// from the source vertex to the RED colored vertices and
127+
// from the BLUE colored vertices to the sink vertex
128+
void Graph::AddAdditionalEdges()
129+
{
130+
// Resizing the residual graph to accomodate space for the new edges
131+
for (auto& edge : this->_residualGraph)
132+
{
133+
edge.resize(this->_noOfVertices, 0);
134+
}
135+
136+
this->_parent.resize(this->_noOfVertices, -1);
137+
this->_visited.resize(this->_noOfVertices, false);
138+
this->_color.resize(this->_noOfVertices, WHITE);
139+
this->_residualGraph.resize(this->_noOfVertices, vector<int>(this->_noOfVertices, 0));
140+
141+
// Creating the additional edges
142+
for (int node = 0; node < this->_source; node++)
143+
{
144+
// From source vertex --> RED colored vertices
145+
if (this->_color[node] == RED)
146+
{
147+
this->_residualGraph[this->_source][node] = 1;
148+
}
149+
150+
// From BLUE colored vertices --> sink vertex
151+
else if (this->_color[node] == BLUE)
152+
{
153+
this->_residualGraph[node][this->_sink] = 1;
154+
}
155+
}
156+
}
157+
158+
// Implementation of BreadthFirstSearch for EdmondsKarp algorithm to find the path from source to sink
159+
bool Graph::BreadthFirstSearch()
160+
{
161+
// Resetting the visited values
162+
fill(this->_visited.begin(), this->_visited.end(), false);
163+
164+
// Resetting the parent values
165+
fill(this->_parent.begin(), this->_parent.end(), -1);
166+
167+
queue<int> nodeQueue;
168+
nodeQueue.push(this->_source);
169+
this->_visited[this->_source] = true;
170+
171+
while (!nodeQueue.empty())
172+
{
173+
int nodeU = nodeQueue.front();
174+
nodeQueue.pop();
175+
176+
for (int nodeV = 0; nodeV < this->_noOfVertices; nodeV++)
177+
{
178+
if (!this->_visited[nodeV] && this->_residualGraph[nodeU][nodeV] > 0)
179+
{
180+
this->_parent[nodeV] = nodeU;
181+
this->_visited[nodeV] = true;
182+
nodeQueue.push(nodeV);
183+
}
184+
}
185+
}
186+
187+
// Returning the visited value of the sink vertex, initially it was set to false
188+
return this->_visited[this->_sink];
189+
}
190+
191+
// Graph Public Member Methods
192+
void Graph::CreateGraph(int noOfVertices)
193+
{
194+
this->_noOfVertices = noOfVertices;
195+
this->_maximumFlow = 0;
196+
this->_flagParallelEdges = false;
197+
this->_adjMatrix = vector<vector<int>>(this->_noOfVertices, vector<int>(this->_noOfVertices, 0));
198+
this->_parent = vector<int>(this->_noOfVertices, -1);
199+
this->_visited = vector<bool>(this->_noOfVertices, false);
200+
this->_color = vector<int>(this->_noOfVertices, WHITE);
201+
}
202+
203+
void Graph::PushDirectedEdge(int valueU, int valueV)
204+
{
205+
this->_adjMatrix[valueU][valueV] = 1;
206+
}
207+
208+
int Graph::FindMaximumBipartiteMatching()
209+
{
210+
// Resolving all the parallel edges if present
211+
this->ResolveAntiParallelEdges();
212+
this->_residualGraph = this->_adjMatrix;
213+
214+
this->ColorGraph();
215+
216+
this->_source = this->_noOfVertices;
217+
this->_noOfVertices++;
218+
this->_sink = this->_noOfVertices;
219+
this->_noOfVertices++;
220+
221+
this->AddAdditionalEdges();
222+
223+
// While there exists a path p from source to sink in the residual network G'
224+
while (this->BreadthFirstSearch())
225+
{
226+
int augmentedPathFlow = 1;
227+
228+
// No need to find the minimum amount of augmentedPathFlow as like standard EdmondsKarp algorithm
229+
// as here capacity of each edges is 1
230+
for (int nodeV = this->_sink; nodeV != this->_source; nodeV = this->_parent[nodeV])
231+
{
232+
int nodeU = this->_parent[nodeV];
233+
this->_residualGraph[nodeU][nodeV] -= augmentedPathFlow;
234+
this->_residualGraph[nodeV][nodeU] += augmentedPathFlow;
235+
}
236+
this->_maximumFlow += augmentedPathFlow;
237+
}
238+
239+
return this->_maximumFlow;
240+
}
241+
242+
// This method is used for finding the matchings
243+
vector<vector<int>> Graph::GetMatchings()
244+
{
245+
for (int nodeU = 0; nodeU < this->_adjMatrix.size(); nodeU++)
246+
{
247+
for (int nodeV = 0; nodeV < this->_adjMatrix.size(); nodeV++)
248+
{
249+
// Check if the nodeU and nodeV are not source or sink
250+
// and there is a flow of 1 unit from nodeU --> nodeV
251+
// which means nodeU --> nodeV is being used for the maximum flow (maximum matching)
252+
if ((nodeU != this->_source || nodeU != this->_sink || nodeV != this->_source || nodeV != this->_sink)
253+
&&
254+
(this->_adjMatrix[nodeU][nodeV] - this->_residualGraph[nodeU][nodeV]) == 1)
255+
{
256+
this->_matchings.push_back({ nodeU, nodeV });
257+
}
258+
}
259+
}
260+
261+
return this->_matchings;
262+
}
263+
}

SourceCodes/0003_Graph/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ set(0003GRAPH_SOURCES
1616
0014_AllPairsShortestPathsJohnson.cc
1717
0015_MaximumFlowFordFulkerson.cc
1818
0016_MaximumFlowEdmondsKarp.cc
19+
0017_MaximumBipartiteMatching.cc
1920

2021
)
2122

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include<gtest/gtest.h>
2+
#include "../Headers/0003_Graph/0017_MaximumBipartiteMatching.h"
3+
#include "../0000_CommonUtilities/UnitTestHelper.h"
4+
5+
namespace MaximumBipartiteMatching
6+
{
7+
TEST(MaximumBipartiteMatching, SimpleGraph)
8+
{
9+
// Arrange
10+
Graph graph;
11+
UnitTestHelper unitTestHelper;
12+
int noOfVertices = 9;
13+
int expectedMaximumMatching = 3;
14+
string expectedMatchings = "[0 1][2 6][3 5]";
15+
16+
// Act
17+
graph.CreateGraph(noOfVertices);
18+
19+
graph.PushDirectedEdge(0, 1);
20+
graph.PushDirectedEdge(2, 1);
21+
graph.PushDirectedEdge(2, 6);
22+
graph.PushDirectedEdge(3, 5);
23+
graph.PushDirectedEdge(3, 6);
24+
graph.PushDirectedEdge(3, 7);
25+
graph.PushDirectedEdge(4, 6);
26+
graph.PushDirectedEdge(8, 6);
27+
28+
int actualMaximumMatching = graph.FindMaximumBipartiteMatching();
29+
vector<vector<int>> actualMatchings = graph.GetMatchings();
30+
31+
// Assert
32+
ASSERT_EQ(expectedMaximumMatching, actualMaximumMatching);
33+
ASSERT_EQ(expectedMatchings, unitTestHelper.SerializeVectorToString(actualMatchings));
34+
}
35+
}

Tests/0003_Graph/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_executable(
2828
0014_AllPairsShortestPathsJohnsonTest.cc
2929
0015_MaximumFlowFordFulkersonTest.cc
3030
0016_MaximumFlowEdmondsKarpTest.cc
31+
0017_MaximumBipartiteMatchingTest.cc
3132
)
3233

3334
target_link_libraries(

0 commit comments

Comments
 (0)