I actually had to revisit this problem with slightly different input, and once again I struggled. If I represent an undirected graph like this:
<link end1="1" end2="2"/>
<link end1="1" end2="3"/>
<link end1="1" end2="4"/>
<link end1="1" end2="5"/>
…then it's a little harder to go from one node to its connected nodes, because the information I need is elsewhere in the XML document (i.e. not in a child node of the <node> element) . To index any node element, I'd have to navigate up the tree, and back down to the links. What's worse, I'd have to scan all the links for every node. So the solution involves some indirection. First I declare these two mappings:
<xsl:key name="left" match="/graph/links/link" use="@end1"/>
<xsl:key name="right" match="/graph/links/link" use="@end2"/>
Now I map each node (its ID, actually; remember that the key has to be of an atomic type) to its connected nodes by going through these mappings (such a recursive definition is not possible in XSLT 1.0, I believe). Note that for links that were indexed by @end1, I'm interested in the value of @end2, and v.v.
<xsl:key name="connected" match="node" use="key('left', @id)/@end2, key('right', @id)/@end1"/>
Finally, I can use this mapping:
<xsl:value-of select="key('connected', @id)/@id"/>
I believe that these are all the data structures I need. Now I can implement the "strongly connected components" algorithm as I did below, but using set operators on the nodes.