@@ -13,7 +13,7 @@ public class HopcroftKarpMatching<T>
1313 /// <summary>
1414 /// Returns a list of Max BiPartite Match Edges.
1515 /// </summary>
16- public List < MatchEdge < T > > GetMaxBiPartiteMatching ( IGraph < T > graph )
16+ public HashSet < MatchEdge < T > > GetMaxBiPartiteMatching ( IGraph < T > graph )
1717 {
1818 //check if the graph is BiPartite by coloring 2 colors
1919 var mColorer = new MColorer < T , int > ( ) ;
@@ -25,182 +25,195 @@ public List<MatchEdge<T>> GetMaxBiPartiteMatching(IGraph<T> graph)
2525 }
2626
2727 return getMaxBiPartiteMatching ( graph , colorResult . Partitions ) ;
28-
2928 }
3029
3130 /// <summary>
3231 /// Get Max Match from Given BiPartitioned Graph.
3332 /// </summary>
34- private List < MatchEdge < T > > getMaxBiPartiteMatching ( IGraph < T > graph ,
33+ private HashSet < MatchEdge < T > > getMaxBiPartiteMatching ( IGraph < T > graph ,
3534 Dictionary < int , List < T > > partitions )
3635 {
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 > ( ) ;
3940
41+ var freeVerticesOnRight = bfs ( graph , partitions , leftToRightMatchEdges , rightToLeftMatchEdges ) ;
4042 //while there is an augmenting Path
41- while ( bfs ( graph , partitions , leftMatch , rightMatch ) )
43+ while ( freeVerticesOnRight . Count > 0 )
4244 {
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 )
4449 {
45- if ( ! rightMatch . ContainsKey ( vertex ) )
50+ var path = dfs ( graph ,
51+ leftToRightMatchEdges , rightToLeftMatchEdges , vertex , default , visited , true ) ;
52+
53+ if ( path != null )
4654 {
47- var visited = new HashSet < T > { vertex } ;
55+ union ( paths , path ) ;
56+ }
57+ }
4858
49- var pathResult = dfs ( graph . GetVertex ( vertex ) ,
50- leftMatch , rightMatch , visited , true ) ;
59+ xor ( matches , paths , leftToRightMatchEdges , rightToLeftMatchEdges ) ;
5160
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 )
5495 {
55- if ( pair . isRight )
96+ if ( visited . Contains ( leftToRightEdge . TargetVertexKey ) )
5697 {
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 ) ;
59105 }
60106 else
61107 {
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+ }
64117 }
118+
119+ visited . Add ( leftToRightEdge . TargetVertexKey ) ;
65120 }
66121 }
67-
68122 }
69-
70123 }
71124
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 ;
80126 }
81127
82128 /// <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.
84131 /// </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 )
86139 {
87- public T A { get ; }
88- public T B { get ; }
89- public bool isRight { get ; }
140+ var currentIsLeft = ! currentIsRight ;
90141
91- public PathResult ( T a , T b , bool isRight )
142+ if ( visited . Contains ( current ) )
92143 {
93- A = a ;
94- B = b ;
95- this . isRight = isRight ;
144+ return null ;
96145 }
97- }
98146
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 ) )
106149 {
107- return new List < PathResult > ( ) ;
150+ visited . Add ( current ) ;
151+ return new HashSet < MatchEdge < T > > ( ) { new MatchEdge < T > ( current , previous ) } ;
108152 }
109153
110- foreach ( var edge in current . Edges )
154+ //right to left should be unmatched edges
155+ if ( currentIsRight && ! rightToLeftMatchEdges . ContainsKey ( current ) )
111156 {
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 )
124158 {
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+ }
126166 }
167+ }
127168
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 )
143173 {
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+ }
145181 }
146-
147- return pathResult ;
148-
149182 }
150183
151184 return null ;
152185 }
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 )
162188 {
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 )
169190 {
170- leftGroup . Add ( vertex ) ;
171- //if vertex is free
172- if ( ! leftMatch . ContainsKey ( vertex ) )
191+ if ( ! paths . Contains ( item ) )
173192 {
174- queue . Enqueue ( vertex ) ;
175- visited . Add ( vertex ) ;
193+ paths . Add ( item ) ;
176194 }
177195 }
196+ }
178197
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 )
180202 {
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 ) )
186204 {
187- return true ;
205+ matches . Remove ( item ) ;
206+ leftToRightMatchEdges . Remove ( item . Source ) ;
207+ rightToLeftMatchEdges . Remove ( item . Target ) ;
188208 }
189-
190- foreach ( var edge in graph . GetVertex ( current ) . Edges )
209+ else
191210 {
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 ) ;
199214 }
200-
201215 }
202-
203- return false ;
204216 }
217+
205218 }
206219}
0 commit comments