{"id":538,"date":"2007-10-30T21:09:27","date_gmt":"2007-10-30T21:09:27","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/10\/30\/computing-strongly-connected-components\/"},"modified":"2007-10-30T21:09:27","modified_gmt":"2007-10-30T21:09:27","slug":"computing-strongly-connected-components","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2007\/10\/30\/computing-strongly-connected-components\/","title":{"rendered":"Computing Strongly Connected Components"},"content":{"rendered":"<p> As promised, today I&#8217;m going to talk about how to compute the strongly connected components<br \/>\nof a directed graph. I&#8217;m going to go through one method, called Kosaraju&#8217;s algorithm, which is<br \/>\nthe easiest to understand. It&#8217;s possible to do better that Kosaraju&#8217;s by a factor of 2, using<br \/>\nan algorithm called Tarjan&#8217;s algorithm, but Tarjan&#8217;s is really just a variation on the theme<br \/>\nof Kosaraju&#8217;s.<\/p>\n<p><!--more--><\/p>\n<p> Kosaraju&#8217;s algorithm is amazingly simple. It uses a very clever trick based on the fact that<br \/>\nif you reverse all of the edges in a graph, the resulting graph has the same strongly connected components as the original.  So using that, we can get the SCCs by doing a forward traversal to find an ordering of vertices, then doing a traversal of the reverse of the graph in the order generated by the first traversal.<\/p>\n<p> That may sound a bit mysterious, but it&#8217;s really very simple. Take the graph G, and<br \/>\ndo a recursive depth-first traversal of the graph, along with an initially empty stack of vertices. As the recursion started at a vertex V finishes, push V onto the stack. At the end of the traversal, you&#8217;ll<br \/>\nhave all of the vertices of G in the stack. The order of the reverse traversal will be starting<br \/>\nwith the vertices on the top of that stack.<\/p>\n<p> So you reverse all of the edges in the graph, creating the reverse graph, G&#8217;. Start with the<br \/>\nvertex on top of the stack, and to a traversal from that vertex. All of the nodes reachable from<br \/>\nthat vertex form one strongly connected component. Remove everything in that SCC from the stack, and<br \/>\nthen repeat the process with the new top of the stack. When the stack is empty, you&#8217;ll have accumulated all of the SCCs.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"example-scc-graph.png\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_290.png?resize=238%2C152\" width=\"238\" height=\"152\" class=\"inset right\" \/><\/p>\n<p> As usual, things are a whole lot clearer with an example. Let&#8217;s do that using the graph to the right as an example.<\/p>\n<p> First, we do a depth-first traversal of the graph, putting vertices on a stack as we finish the<br \/>\nrecursive step started with that node. We&#8217;ll start the traversal with &#8220;A&#8221;. A,F,E,C,G,H &#8211; H is finished,<br \/>\nso it goes on the stack. Then G goes on the stack; then C, then E, then F, and we&#8217;re back to A. So<br \/>\nat this point, the stack is [H, G, C, E, F]. Then we keep going from A: A, B, D. D is done, so it goes<br \/>\non the stack; then B, then A. So the final stack is [H, G, C, E, F, D, B, A]<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"example-scc-graph-rev.png\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_291.png?resize=238%2C152\" width=\"238\" height=\"152\" class=\"inset right\" \/><\/p>\n<p> Then, we reverse the graph, and start traversing from whatever is on top of the stack. So first, we start from A in the reversed graph. That gives us A, D, B. So {A, D, B} form one strongly connected<br \/>\ncomponent. Now, the top of the stack that hasn&#8217;t been traversed yet is F. So we do F, E. {F,E} is<br \/>\nanother SCC. The remaining stack is now [H, G, C], which traverses straightforwardly as C, H, G. So<br \/>\nwe end up with {A, B, D}, {E, F}, and {C, G, H} as our SCCs.<\/p>\n<p> What&#8217;s the time complexity? We need to do two traversals, and one graph reversal. If we&#8217;re using an adjacency list representation, we can create the reverse graph while we do the first traversal, so it&#8217;s really just two depth-first traversals. So two times the complexity of a traversal; in adjacency list representation, traversal is O(V+E), where E is the number of edges &#8211; so Kosaraju&#8217;s SCC algorithm is also O(V+E). (If you use an adjacency matrix, then it&#8217;s O(N<sup>2<\/sup>).)<\/p>\n<p> Can we do better than that? Order of complexity, no. You can&#8217;t do better than the cost of a single traversal of the graph. You can eliminate the second traversal. Instead of doing that second<br \/>\ntraversal, you can do the forward traversal using the stack, and then pull things off the stack<br \/>\nchecking if they are the root of a strongly connected component, and if so, removing all members<br \/>\nof that component. Tarjan&#8217;s algorithm works this way, and ends up doing one traversal, but considering<br \/>\neach edge in the graph twice. So it&#8217;s slightly, but not dramatically faster. It&#8217;s generally preferred<br \/>\njust because it avoids the computation of the reverse graph.<\/p>\n<p> In terms of what we were talking about in <a href=\"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/10\/making-graph-algorithms-fast-using-strongly-connected-components\">the last post,<\/a> this is good news. The computation of the SCCs is quite fast &#8211; quite a lot better than NP-complete. So we can, feasibly, use the division into SCCs as the basis of parallelization of graph algorithms.<\/p>\n<p><em>As usual, all diagrams were drawn using <a href=\"http:\/\/www.omnigroup.com\/applications\/omnigraffle\/\">OmniGraffle.<\/a><\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As promised, today I&#8217;m going to talk about how to compute the strongly connected components of a directed graph. I&#8217;m going to go through one method, called Kosaraju&#8217;s algorithm, which is the easiest to understand. It&#8217;s possible to do better that Kosaraju&#8217;s by a factor of 2, using an algorithm called Tarjan&#8217;s algorithm, but Tarjan&#8217;s [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[25],"tags":[],"class_list":["post-538","post","type-post","status-publish","format-standard","hentry","category-graph-theory"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-8G","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/538","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=538"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/538\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=538"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=538"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=538"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}