Skip to content

Commit

Permalink
cncom.h: Tarjan's Algorithm ready
Browse files Browse the repository at this point in the history
  • Loading branch information
zzjjzzgggg committed Apr 1, 2018
1 parent 38a1b00 commit 619b762
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
BasedOnStyle: Google
ColumnLimit: 79
ColumnLimit: 80
IndentWidth: 4
---
Language: Cpp
Expand Down
2 changes: 1 addition & 1 deletion graph/bfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "comm.h"

/**
* BFS over directed graphs.
* BFS over directed graphs, i.e., only search from out-neighbors of each node.
*/
template <class Graph>
class DirBFS {
Expand Down
136 changes: 136 additions & 0 deletions graph/cncom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Copyright (C) by J.Z. (03/31/2018 16:50)
* Distributed under terms of the MIT license.
*/

#ifndef __CNCOM_H__
#define __CNCOM_H__

#include "comm.h"

namespace graph {

/**
* Strongly connected componetn DFS visitor. Using Tarjan's SCC Algorithm. Refer
* to Knuth's book "The Stanford Graph Base", pp.512-519.
*/
template <class Graph>
class SCCVisitor {
public:
class Element {
public:
int id, parent, rank, min, edge, deg;

public:
Element(int v = -1, int r = -1, int d = 0)
: id(v), parent(-1), rank(r), min(v), edge(0), deg(d) {}
}; /* Node */

private:
const Graph& graph_;
int rank_;
std::unordered_map<int, Element> record_;
std::stack<int> stack_;

public:
std::vector<std::pair<int, int>> nd_cc_vec_; // node-component pairs

public:
SCCVisitor(const Graph& graph) : graph_(graph), rank_(0) {
nd_cc_vec_.reserve(graph_.getNodes());
}

void performDFS() {
for (auto it = graph_.beginNI(); it != graph_.endNI(); it++) {
int v = it->first;
if (record_.find(v) == record_.end()) {
makeActive(v);
while (v != -1) v = explore(v);
}
}
// topology sort
std::reverse(nd_cc_vec_.begin(), nd_cc_vec_.end());
}

/**
* Make vertex v active.
*/
void makeActive(const int v, const int parent = -1) {
record_[v] = Element(v, ++rank_, graph_[v].getOutDeg());
if (parent != -1) record_[v].parent = parent;
stack_.push(v);
}

/**
* Explore one step from current vertex v, possible move to another current
* vertex and calling it v. Return the next vertex to be explored.
*/
int explore(const int v) {
Element& ve = record_[v];
if (ve.edge != ve.deg) { // v has untagged edge
const int u = graph_[v].getOutNbr(ve.edge++);
if (record_.find(u) != record_.end()) { // seen u before
int u_rank = record_[u].rank, v_min_rank = record_[ve.min].rank;
if (u_rank < v_min_rank) ve.min = u;
return v;
} else { // u is presently unseen
makeActive(u, v);
// u become the current vertex, i.e., the node to be explored
return u;
}
} else { // all edges from v are tagged, so v matures
int u = ve.parent;
if (ve.min == v) { // subtree with root v forms a SCC
finish(v);
} else { // the edge from u to v has just matured, make v.min
// visible from u
int v_min_rank = record_[ve.min].rank, &u_min = record_[u].min,
u_min_rank = record_[u_min].rank;
if (v_min_rank < u_min_rank) u_min = ve.min;
}
return u;
}
}

/**
* Remove v and all its successors on the active stack from the tree, and
* mark them as a SCC.
*/
void finish(const int v) {
while (true) {
int t = stack_.top();
stack_.pop();
nd_cc_vec_.emplace_back(t, v);
record_[t].rank = INT_MAX;
record_[t].parent = v;
if (t == v) break;
}
}

/**
* Print out one representative edge between two SCCs.
*/
void getConnections(std::vector<std::pair<int, int>>& cc_edge_vec) {
int cur_cc = -1;
std::unordered_set<int> discovered_ccs;
for (auto&& it = nd_cc_vec_.begin(); it != nd_cc_vec_.end(); it++) {
int v = it->first;
int cc_from = it->second;
if (cc_from != cur_cc) {
cur_cc = cc_from;
discovered_ccs.clear();
}
discovered_ccs.insert(cc_from);
for (int d = 0; d < record_[v].deg; d++) {
int cc_to = record_[graph_[v].getOutNbr(d)].parent;
if (discovered_ccs.find(cc_to) == discovered_ccs.end()) {
discovered_ccs.insert(cc_to);
cc_edge_vec.emplace_back(cc_from, cc_to);
}
}
}
}
};

} /* namespace graph */
#endif /* __CNCOM_H__ */
1 change: 1 addition & 0 deletions graph/comm.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>
#include <climits>
Expand Down
2 changes: 2 additions & 0 deletions graph/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
#include "triad.h"
#include "network.h"

#include "cncom.h"

#endif /* __GRAPH_H__ */
61 changes: 57 additions & 4 deletions test/test_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,75 @@

using namespace graph;

int main(int argc, char* argv[]) {
osutils::Timer tm;
/**
* test SCC on directed graph
*/
void test_scc() {
DGraph g;
g.addEdge(0, 1);
g.addEdge(0, 5);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 2);
g.addEdge(3, 5);
g.addEdge(4, 2);
g.addEdge(4, 3);
g.addEdge(5, 4);
g.addEdge(6, 0);
g.addEdge(6, 4);
g.addEdge(6, 9);
g.addEdge(7, 6);
g.addEdge(7, 8);
g.addEdge(8, 7);
g.addEdge(8, 9);
g.addEdge(9, 10);
g.addEdge(9, 11);
g.addEdge(10, 12);
g.addEdge(11, 4);
g.addEdge(11, 12);
g.addEdge(12, 9);
SCCVisitor visitor(g);
visitor.performDFS();
auto& nd_cc_vec = visitor.nd_cc_vec_;
int cc = -1;
for (auto it = nd_cc_vec.begin(); it!=nd_cc_vec.end(); it++) {
int v = it->first, c = it->second;
if (c != cc) {
cc = c;
printf("\nSCC[%d]: ", c);
}
printf(" %d", v);
}
printf("\n");

std::vector<std::pair<int, int>> cc_edge_vec;
visitor.getConnections(cc_edge_vec);
for (auto it = cc_edge_vec.begin(); it!=cc_edge_vec.end(); it++) {
printf("%d --> %d\n", it->first, it->second);
}
}

void test_net() {
DatNet<int, int, std::greater<std::pair<int, int>>> net;
net.addEdge(1, 2, 1);
net.addEdge(2, 3, 1);
net.addEdge(2, 4, 2);
net.addEdge(4, 5, 1);
printf("%d, %d\n", net.getNodes(), net.getEdges());

for (auto&& ei=net.beginEI(); ei!=net.endEI(); ei++) {
for (auto&& ei = net.beginEI(); ei != net.endEI(); ei++) {
printf("%d, %d, %d\n", ei.getSrcID(), ei.getDstID(), ei.getDat());
}

for (auto&& ni=net.beginNI(); ni!=net.endNI(); ni++) {
for (auto&& ni = net.beginNI(); ni != net.endNI(); ni++) {
printf("%d, %d\n", ni->first, ni->second.getOutDeg());
}
}

int main(int argc, char* argv[]) {
// osutils::Timer tm;

test_scc();

return 0;
}

0 comments on commit 619b762

Please sign in to comment.