J - 19 15 char
J doesn't have a dictionary type, and is generally garbage with representing things the way people are used to in other languages. This is a bit idiosyncratic with how it represents things, but that's because J and graphs don't blend well, and not because I'm looking for optimal ways of representing directed graphs. But even if it doesn't count, I think it's a concise, interesting algorithm.
~.@(],;@:{~)^:_
This is a function which takes a graph on the left and a list of nodes to start with on the right.
Graphs are lists of boxed lists, and the nodes are indices into the list that represents the graph. J indexes from zero, so nodes are nonnegative integers, but you can skip 0 and pretend to have 1-based indexing by using an empty list (''
) in the 0 place. (Indented lines are input to the J console, lines flush with the margin are output.)
2 4;1 2 4;0;0 2;1 NB. {0: [2, 4], 1: [1, 2, 4], 2: [0], 3: [0, 2], 4:[1]}
+---+-----+-+---+-+
|2 4|1 2 4|0|0 2|1|
+---+-----+-+---+-+
'';2 3;3;1;2 NB. {1: [2, 3], 2: [3], 3: [1], 4:[2]}
++---+-+-+-+
||2 3|3|1|2|
++---+-+-+-+
'';'';2;9;1 7 8 9;3 6;4 8;7;'';3;'';7 NB. the big Mathematica example
+++-+-+-------+---+---+-++-++-+
|||2|9|1 7 8 9|3 6|4 8|7||3||7|
+++-+-+-------+---+---+-++-++-+
Lists of numbers are just space-separated lists of numbers. In general a scalar (just one number in a row) is not the same as a one-item list, but I use only safe operations that end up promoting scalars to one-item lists, so everything's cool.
The function explained (supposing we invoke as graph ~.@(],;@:{~)^:_ nodes
):
{~
- For each node in nodes
, take the list of adjacent nodes from graph
.
;@:
- Turn the list of adjacencies into a list of nodes.
],
- Prepend nodes
to this list.
~.@
- Throw out any duplicates.
^:_
- Repeat this procedure with the same graph
until it results in no change to the right hand argument, i.e. there are no new nodes to discover.
The verb in action:
('';2 3;3;1;2) ~.@(],;@:{~)^:_ (1) NB. example in question
1 2 3
f =: ~.@(],;@:{~)^:_ NB. name for convenience
(2 4;1 2 4;0;0 2;1) f 2
2 0 4 1
('';'';2;9;1 7 8 9;3 6;4 8;7;'';3;'';7) f 4 NB. Mathematica example
4 1 7 8 9 3
If you don't like this graph representation, here's a couple quick verbs for converting to/from adjacency matrices.
a=:<@#i.@# NB. adj.mat. to graph
mat=:0,0,.0 1 1 0,0 0 1 0,1 0 0 0,:0 1 0 0
mat NB. adj.mat. for {1:[2,3],2:[3],3:[1],4:[2]}
0 0 0 0 0
0 0 1 1 0
0 0 0 1 0
0 1 0 0 0
0 0 1 0 0
a mat
++---+-+-+-+
||2 3|3|1|2|
++---+-+-+-+
A=:+/@:=&>/i.@# NB. graph to adj.mat.
A a mat
0 0 0 0 0
0 0 1 1 0
0 0 0 1 0
0 1 0 0 0
0 0 1 0 0
For anyone wondering why the graph is the left argument to the verb, it's because that's the general convention in J: control information on the left, data on the right. Consider list search, list i. item
, and the sequential machine dyad, machine ;: input
.