@@ -13,7 +13,7 @@ public class HopcroftKarpMatching<T>
13
13
/// <summary>
14
14
/// Returns a list of Max BiPartite Match Edges.
15
15
/// </summary>
16
- public List < MatchEdge < T > > GetMaxBiPartiteMatching ( IGraph < T > graph )
16
+ public HashSet < MatchEdge < T > > GetMaxBiPartiteMatching ( IGraph < T > graph )
17
17
{
18
18
//check if the graph is BiPartite by coloring 2 colors
19
19
var mColorer = new MColorer < T , int > ( ) ;
@@ -25,182 +25,195 @@ public List<MatchEdge<T>> GetMaxBiPartiteMatching(IGraph<T> graph)
25
25
}
26
26
27
27
return getMaxBiPartiteMatching ( graph , colorResult . Partitions ) ;
28
-
29
28
}
30
29
31
30
/// <summary>
32
31
/// Get Max Match from Given BiPartitioned Graph.
33
32
/// </summary>
34
- private List < MatchEdge < T > > getMaxBiPartiteMatching ( IGraph < T > graph ,
33
+ private HashSet < MatchEdge < T > > getMaxBiPartiteMatching ( IGraph < T > graph ,
35
34
Dictionary < int , List < T > > partitions )
36
35
{
37
- var leftMatch = new Dictionary < T , T > ( ) ;
38
- var rightMatch = new Dictionary < T , T > ( ) ;
36
+ var matches = new HashSet < MatchEdge < T > > ( ) ;
37
+
38
+ var leftToRightMatchEdges = new Dictionary < T , T > ( ) ;
39
+ var rightToLeftMatchEdges = new Dictionary < T , T > ( ) ;
39
40
41
+ var freeVerticesOnRight = bfs ( graph , partitions , leftToRightMatchEdges , rightToLeftMatchEdges ) ;
40
42
//while there is an augmenting Path
41
- while ( bfs ( graph , partitions , leftMatch , rightMatch ) )
43
+ while ( freeVerticesOnRight . Count > 0 )
42
44
{
43
- foreach ( var vertex in partitions [ 2 ] )
45
+ var visited = new HashSet < T > ( ) ;
46
+ var paths = new HashSet < MatchEdge < T > > ( ) ;
47
+
48
+ foreach ( var vertex in freeVerticesOnRight )
44
49
{
45
- if ( ! rightMatch . ContainsKey ( vertex ) )
50
+ var path = dfs ( graph ,
51
+ leftToRightMatchEdges , rightToLeftMatchEdges , vertex , default , visited , true ) ;
52
+
53
+ if ( path != null )
46
54
{
47
- var visited = new HashSet < T > { vertex } ;
55
+ union ( paths , path ) ;
56
+ }
57
+ }
48
58
49
- var pathResult = dfs ( graph . GetVertex ( vertex ) ,
50
- leftMatch , rightMatch , visited , true ) ;
59
+ xor ( matches , paths , leftToRightMatchEdges , rightToLeftMatchEdges ) ;
51
60
52
- //XOR remaining done here (partially done inside DFS)
53
- foreach ( var pair in pathResult )
61
+ freeVerticesOnRight = bfs ( graph , partitions , leftToRightMatchEdges , rightToLeftMatchEdges ) ;
62
+ }
63
+
64
+ return matches ;
65
+ }
66
+
67
+ /// <summary>
68
+ /// Returns list of free vertices on right if there is an augmenting Path from left to right.
69
+ /// An augmenting path is a path which starts from a free vertex
70
+ /// and ends at a free vertex via UnMatched (left -> right) and Matched (right -> left) edges alternatively.
71
+ /// </summary>
72
+ private List < T > bfs ( IGraph < T > graph ,
73
+ Dictionary < int , List < T > > partitions ,
74
+ Dictionary < T , T > leftToRightMatchEdges , Dictionary < T , T > rightToLeftMatchEdges )
75
+ {
76
+ var queue = new Queue < T > ( ) ;
77
+ var visited = new HashSet < T > ( ) ;
78
+
79
+ var freeVerticesOnRight = new List < T > ( ) ;
80
+
81
+ foreach ( var vertex in partitions [ 1 ] )
82
+ {
83
+ //if this left vertex is free
84
+ if ( ! leftToRightMatchEdges . ContainsKey ( vertex ) )
85
+ {
86
+ queue . Enqueue ( vertex ) ;
87
+ visited . Add ( vertex ) ;
88
+
89
+ while ( queue . Count > 0 )
90
+ {
91
+ var current = queue . Dequeue ( ) ;
92
+
93
+ //unmatched edges left to right
94
+ foreach ( var leftToRightEdge in graph . GetVertex ( current ) . Edges )
54
95
{
55
- if ( pair . isRight )
96
+ if ( visited . Contains ( leftToRightEdge . TargetVertexKey ) )
56
97
{
57
- rightMatch . Add ( pair . A , pair . B ) ;
58
- leftMatch . Add ( pair . B , pair . A ) ;
98
+ continue ;
99
+ }
100
+
101
+ //checking if this right vertex is free
102
+ if ( ! rightToLeftMatchEdges . ContainsKey ( leftToRightEdge . TargetVertex . Key ) )
103
+ {
104
+ freeVerticesOnRight . Add ( leftToRightEdge . TargetVertex . Key ) ;
59
105
}
60
106
else
61
107
{
62
- leftMatch . Add ( pair . A , pair . B ) ;
63
- rightMatch . Add ( pair . B , pair . A ) ;
108
+ foreach ( var rightToLeftEdge in leftToRightEdge . TargetVertex . Edges )
109
+ {
110
+ //matched edge right to left
111
+ if ( leftToRightMatchEdges . ContainsKey ( rightToLeftEdge . TargetVertexKey )
112
+ && ! visited . Contains ( rightToLeftEdge . TargetVertexKey ) )
113
+ {
114
+ queue . Enqueue ( rightToLeftEdge . TargetVertexKey ) ;
115
+ }
116
+ }
64
117
}
118
+
119
+ visited . Add ( leftToRightEdge . TargetVertexKey ) ;
65
120
}
66
121
}
67
-
68
122
}
69
-
70
123
}
71
124
72
- //now gather all
73
- var result = new List < MatchEdge < T > > ( ) ;
74
-
75
- foreach ( var item in leftMatch )
76
- {
77
- result . Add ( new MatchEdge < T > ( item . Key , item . Value ) ) ;
78
- }
79
- return result ;
125
+ return freeVerticesOnRight ;
80
126
}
81
127
82
128
/// <summary>
83
- /// Trace Path for DFS
129
+ /// Find an augmenting path that start from a given free vertex on right and ending
130
+ /// at a free vertex on left. Return the matching edges along that path.
84
131
/// </summary>
85
- private class PathResult
132
+ private HashSet < MatchEdge < T > > dfs ( IGraph < T > graph ,
133
+ Dictionary < T , T > leftToRightMatchEdges ,
134
+ Dictionary < T , T > rightToLeftMatchEdges ,
135
+ T current ,
136
+ T previous ,
137
+ HashSet < T > visited ,
138
+ bool currentIsRight )
86
139
{
87
- public T A { get ; }
88
- public T B { get ; }
89
- public bool isRight { get ; }
140
+ var currentIsLeft = ! currentIsRight ;
90
141
91
- public PathResult ( T a , T b , bool isRight )
142
+ if ( visited . Contains ( current ) )
92
143
{
93
- A = a ;
94
- B = b ;
95
- this . isRight = isRight ;
144
+ return null ;
96
145
}
97
- }
98
146
99
- private List < PathResult > dfs ( IGraphVertex < T > current ,
100
- Dictionary < T , T > leftMatch , Dictionary < T , T > rightMatch ,
101
- HashSet < T > visitPath ,
102
- bool isRightSide )
103
- {
104
- if ( ! leftMatch . ContainsKey ( current . Key )
105
- && ! isRightSide )
147
+ //free vertex on left found!
148
+ if ( currentIsLeft && ! leftToRightMatchEdges . ContainsKey ( current ) )
106
149
{
107
- return new List < PathResult > ( ) ;
150
+ visited . Add ( current ) ;
151
+ return new HashSet < MatchEdge < T > > ( ) { new MatchEdge < T > ( current , previous ) } ;
108
152
}
109
153
110
- foreach ( var edge in current . Edges )
154
+ //right to left should be unmatched edges
155
+ if ( currentIsRight && ! rightToLeftMatchEdges . ContainsKey ( current ) )
111
156
{
112
- //do not re-visit ancestors in current DFS tree
113
- if ( visitPath . Contains ( edge . TargetVertexKey ) )
114
- {
115
- continue ;
116
- }
117
-
118
- if ( ! visitPath . Contains ( edge . TargetVertexKey ) )
119
- {
120
- visitPath . Add ( edge . TargetVertexKey ) ;
121
- }
122
- var pathResult = dfs ( edge . TargetVertex , leftMatch , rightMatch , visitPath , ! isRightSide ) ;
123
- if ( pathResult == null )
157
+ foreach ( var edge in graph . GetVertex ( current ) . Edges )
124
158
{
125
- continue ;
159
+ var result = dfs ( graph , leftToRightMatchEdges , rightToLeftMatchEdges , edge . TargetVertexKey , current , visited , ! currentIsRight ) ;
160
+ if ( result != null )
161
+ {
162
+ result . Add ( new MatchEdge < T > ( edge . TargetVertexKey , current ) ) ;
163
+ visited . Add ( current ) ;
164
+ return result ;
165
+ }
126
166
}
167
+ }
127
168
128
- //XOR (partially done here by removing same edges)
129
- //other part of XOR (adding new ones) is done after DFS method is finished
130
- if ( leftMatch . ContainsKey ( current . Key )
131
- && leftMatch [ current . Key ] . Equals ( edge . TargetVertexKey ) )
132
- {
133
- leftMatch . Remove ( current . Key ) ;
134
- rightMatch . Remove ( edge . TargetVertexKey ) ;
135
- }
136
- else if ( rightMatch . ContainsKey ( current . Key )
137
- && rightMatch [ current . Key ] . Equals ( edge . TargetVertexKey ) )
138
- {
139
- rightMatch . Remove ( current . Key ) ;
140
- leftMatch . Remove ( edge . TargetVertexKey ) ;
141
- }
142
- else
169
+ //left to right should be matched edges
170
+ if ( currentIsLeft && leftToRightMatchEdges . ContainsKey ( current ) )
171
+ {
172
+ foreach ( var edge in graph . GetVertex ( current ) . Edges )
143
173
{
144
- pathResult . Add ( new PathResult ( current . Key , edge . TargetVertexKey , isRightSide ) ) ;
174
+ var result = dfs ( graph , leftToRightMatchEdges , rightToLeftMatchEdges , edge . TargetVertexKey , current , visited , ! currentIsRight ) ;
175
+ if ( result != null )
176
+ {
177
+ result . Add ( new MatchEdge < T > ( current , edge . TargetVertexKey ) ) ;
178
+ visited . Add ( current ) ;
179
+ return result ;
180
+ }
145
181
}
146
-
147
- return pathResult ;
148
-
149
182
}
150
183
151
184
return null ;
152
185
}
153
-
154
- /// <summary>
155
- /// Returns true if there is an augmenting Path from left to right.
156
- /// An augmenting path is a path which starts from a free vertex
157
- /// and ends at a free vertex via Matched/UnMatched edges alternatively.
158
- /// </summary>
159
- private bool bfs ( IGraph < T > graph ,
160
- Dictionary < int , List < T > > partitions ,
161
- Dictionary < T , T > leftMatch , Dictionary < T , T > rightMatch )
186
+
187
+ private void union ( HashSet < MatchEdge < T > > paths , HashSet < MatchEdge < T > > path )
162
188
{
163
- var queue = new Queue < T > ( ) ;
164
- var visited = new HashSet < T > ( ) ;
165
-
166
- var leftGroup = new HashSet < T > ( ) ;
167
-
168
- foreach ( var vertex in partitions [ 1 ] )
189
+ foreach ( var item in path )
169
190
{
170
- leftGroup . Add ( vertex ) ;
171
- //if vertex is free
172
- if ( ! leftMatch . ContainsKey ( vertex ) )
191
+ if ( ! paths . Contains ( item ) )
173
192
{
174
- queue . Enqueue ( vertex ) ;
175
- visited . Add ( vertex ) ;
193
+ paths . Add ( item ) ;
176
194
}
177
195
}
196
+ }
178
197
179
- while ( queue . Count > 0 )
198
+ private void xor ( HashSet < MatchEdge < T > > matches , HashSet < MatchEdge < T > > paths ,
199
+ Dictionary < T , T > leftToRightMatchEdges , Dictionary < T , T > rightToLeftMatchEdges )
200
+ {
201
+ foreach ( var item in paths )
180
202
{
181
- var current = queue . Dequeue ( ) ;
182
-
183
- //if vertex is free
184
- if ( ! leftGroup . Contains ( current ) &&
185
- ! rightMatch . ContainsKey ( current ) )
203
+ if ( matches . Contains ( item ) )
186
204
{
187
- return true ;
205
+ matches . Remove ( item ) ;
206
+ leftToRightMatchEdges . Remove ( item . Source ) ;
207
+ rightToLeftMatchEdges . Remove ( item . Target ) ;
188
208
}
189
-
190
- foreach ( var edge in graph . GetVertex ( current ) . Edges )
209
+ else
191
210
{
192
- if ( visited . Contains ( edge . TargetVertexKey ) )
193
- {
194
- continue ;
195
- }
196
-
197
- queue . Enqueue ( edge . TargetVertexKey ) ;
198
- visited . Add ( edge . TargetVertexKey ) ;
211
+ matches . Add ( item ) ;
212
+ leftToRightMatchEdges . Add ( item . Source , item . Target ) ;
213
+ rightToLeftMatchEdges . Add ( item . Target , item . Source ) ;
199
214
}
200
-
201
215
}
202
-
203
- return false ;
204
216
}
217
+
205
218
}
206
219
}
0 commit comments