When I asked in class (this is 2016), whether I should implement the program with integers in the adjacency lists, or pointers, the class chose pointers. Nice. So, I have implemented this version in src/concomp-pointer.cpp. I include the implementation below with extensive inline comments to help you. In some respects, this is a simpler implementation, because it doesn't require a Graph class. This is because the pointers point directly to nodes, and don't require that the nodes be accessible as part of the Graph class.
/* The Node class:
Each node's adjacency list contains pointers to the nodes that are
adjacent to them. This is unlike the code in the main set of
lecture notes, where the adjacency lists contains integers, and
the integers are used as indices to a vector that contains all of
the nodes. */
class Node {
public:
int id;
int component;
vector <Node *> Adj;
};
/* Because our adjacency list contains pointers, we don't need a Graph
class. We don't need to access the vector of nodes, because
we access each node directly in the adjacency list with a pointer.
Contrast this with the other code in the lecture notes, where
DFS() is a method that is part of a Graph class, which is
necessary because you need to use the indices to the vector of
nodes in the graph class.
Also, in this code, I print out the beginning of each DFS call, indenting
it with two spaces for every nested level of recursion. You should take a look
at that printf call. I include it here:
printf("%*sDFS(%d,%d)\n", indent, "", n->id, cn);
The asterisk says to read the field width from the next argument -- that means
that that part of the output will be padded to "indent" spaces. Following the
asterisk is an "s", which means to print out a string (C style).
I give it the empty string. So, if, for example, "indent" is equal to 4, this
will print out four spaces.
*/
void DFS(Node *n, int cn, int indent)
{
size_t i;
printf("%*sDFS(%d,%d)\n", indent, "", n->id, cn);
if (n->component != -1) return;
n->component = cn;
for (i = 0; i < n->Adj.size(); i++) DFS(n->Adj[i], cn, indent+2);
}
/* The main() routine -- this reads in the graph, and then calls the DFS
on each node whose "component" field is -1. At the end, it prints each
node. */
int main()
{
vector <Node *> Nodes; /* These are the nodes. Note that they are pointers. */
int nn;
int f, t;
size_t i, j;
string s;
Node *nf, *nt;
int cn;
getline(cin, s);
sscanf(s.c_str(), "NNODES %d", &nn);
/* Because "Nodes" is a vector of pointers, for each of the i nodes, we need
to allocate it with new. We then set its id, and set its component number
to be negative one. */
Nodes.resize(nn);
for (i = 0; i < Nodes.size(); i++) {
Nodes[i] = new Node;
Nodes[i]->id = i;
Nodes[i]->component = -1;
}
/* Read the edges. We then put each node on the other's adjacency list. Once
again, the nodes are pointers, so it's cleaner to use the temporary variables
"nf" and "nt", which are also pointers. */
while (getline(cin, s)) {
sscanf(s.c_str(), "EDGE %d %d", &f, &t);
nf = Nodes[f];
nt = Nodes[t];
nf->Adj.push_back(nt);
nt->Adj.push_back(nf);
}
/* Do the DFS()'s */
cn = 0;
for (i = 0; i < Nodes.size(); i++) {
if (Nodes[i]->component == -1) {
DFS(Nodes[i], cn, 0);
cn++;
}
}
/* Print out each node. */
for (i = 0; i < Nodes.size(); i++) {
nf = Nodes[i];
printf("%2d: Component:%2d Edges:", nf->id, nf->component);
for (j = 0; j < nf->Adj.size(); j++) {
nt = nf->Adj[j];
printf(" %d", nt->id);
}
printf("\n");
}
return 0;
}
|
This code is also a little different, because it prints out the beginning of each DFS() call, with indentation to illustrate the level of nesting. Read the inline comments to show how I did that using printf().
Here's example output with our two graph files:
UNIX> concomp-pointer < g1.txt
DFS(0,0)
DFS(1,1)
DFS(3,1)
DFS(5,1)
DFS(3,1)
DFS(1,1)
DFS(2,2)
DFS(4,3)
DFS(9,3)
DFS(4,3)
DFS(6,3)
DFS(4,3)
DFS(8,3)
DFS(6,3)
DFS(7,3)
DFS(4,3)
0: Component: 0 Edges:
1: Component: 1 Edges: 3
2: Component: 2 Edges:
3: Component: 1 Edges: 5 1
4: Component: 3 Edges: 9 6 7
5: Component: 1 Edges: 3
6: Component: 3 Edges: 4 8
7: Component: 3 Edges: 4
8: Component: 3 Edges: 6
9: Component: 3 Edges: 4
UNIX> concomp-pointer < g2.txt
DFS(0,0)
DFS(3,0)
DFS(7,0)
DFS(3,0)
DFS(2,0)
DFS(1,0)
DFS(2,0)
DFS(7,0)
DFS(9,0)
DFS(5,0)
DFS(9,0)
DFS(8,0)
DFS(5,0)
DFS(6,0)
DFS(8,0)
DFS(7,0)
DFS(2,0)
DFS(5,0)
DFS(0,0)
DFS(4,1)
0: Component: 0 Edges: 3
1: Component: 0 Edges: 2
2: Component: 0 Edges: 1 7 9
3: Component: 0 Edges: 7 0
4: Component: 1 Edges:
5: Component: 0 Edges: 9 8 7
6: Component: 0 Edges: 8
7: Component: 0 Edges: 3 2 5
8: Component: 0 Edges: 5 6
9: Component: 0 Edges: 5 2
UNIX>