14

I have a list:

B={423, {{53, {39, 65, 423}}, {66, {67, 81, 423}}, {424, {25, 40, 423}}}};

This list can be visualized as a tree using TreeForm[B]:

TreeForm[B]

and I would like to find all possible traversals of this tree:

{{423,53,39},{423,53,65},{423,53,423},{423,66,67},{423,66,81},
 {423,66,423},{423,424,25},{423,424,40},{423,424,423}}

It seems that Subset might be usable, but when I tried Subset[B,{3}], it gave me the null set. Another possible problem with Subset is that it perhaps does not respect the leveling of the tree. I looked at the Combinatorica package, but I don't see a way to traverse the tree -- in the direction from top to bottom -- in all possible ways.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Andrew
  • 10,569
  • 5
  • 51
  • 104
  • 1
    Are you looking for solutions for this specific tree B or for general trees? B has the property that all left branches are numbers. Is that a given? – Sjoerd C. de Vries May 20 '12 at 09:51
  • It is a given that all left branches are numbers (and even more specifically, integers). Thanks. – Andrew May 20 '12 at 15:40

3 Answers3

22

Here is one way:

ClearAll[f];
f[tree_List] := Flatten[f[{}, tree], 1];
f[accum_List, {x_, y_List}] := f[{accum, x}, #] & /@ y;
f[x_, y_] := Flatten[{x, y}];

The usage is

f[B]
Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • 1
    I don't think I can beat this! – rm -rf May 18 '12 at 21:58
  • @R.M. Thanks, coming from you that's quite a compliment! I think this is a good example to illustrate that overloading rule-based definitions can be powerful. – Leonid Shifrin May 18 '12 at 22:00
  • 1
    Uhh whoa! That's my line, when you say something like my comment! Not the other way round :) I've just thrown in the towel at Step 0 :P – rm -rf May 18 '12 at 22:02
  • @R.M. You are being too hard on yourself, I think :) – Leonid Shifrin May 18 '12 at 22:07
  • Is there any way to generalize this? This code works on {423,{{53,{39,65,423}},{66,{67,81,423}},{424,{25,40,423}}}}, but if I input {423,{53,66,424}}, f gives the incorrect {423,{53,66,424}}, rather than {{423,53},{423,66},{423,424}}. Thank you kindly! – Andrew May 19 '12 at 01:08
  • 2
    @Andrew You can replace the first definition for f with f[tree_List] := Flatten[f[{}, tree], {1}];. The additional change as a result of this is that the final output will have all the paths from each "major branch" (or what ever the right term is) grouped together. I actually like it this way and you can flatten this out appropriately in a post-processing step. Run the new definition on B to see what I mean. – rm -rf May 19 '12 at 01:22
  • @R.M is this answer shear genius or do you have a reference to an algorithm? – nilo de roock May 19 '12 at 12:28
  • Perhaps this list is bit general: c = {{0, {{0, 3}, {1, 2}, {2, 1}, {3, 0}}}, {1, {{0, 2}, {1, 1}, {2, 0}}}, {2, {{0, 1}, {1, 0}}}, {3, {{0, 0}}}}; TreeForm f[c] does not work, so maybe f needs to be generalized...any idea? –  May 25 '12 at 18:41
  • @Waqar Your example does not have a root node. This will work: c = {4, {{0, {{0, 3}, {1, 2}, {2, 1}, {3, 0}}},{1, {{0, 2}, {1, 1}, {2, 0}}}, {2, {{0, 1}, {1, 0}}}, {3, {{0, 0}}}}}, where I added 4 as a root node. – Leonid Shifrin May 25 '12 at 20:24
  • hmmm, today spent some time trying to come up with a solution for this question and only now did I realise how clever this is... This is what I was groping for but I couldn't quite write it. – acl May 26 '12 at 14:41
  • 1
    @acl Thanks for the upvote and kind words. The thing is, I was prepared for this question, having done similar things in the past. What may look clever from the outset is often just a result of some practice and preparation, you just have to think in a certain direction. Once you apply some new (to you) technique a few times, it grows on you and leads your thoughts, and then it is not particularly hard. I think this applies to anything (I do know from experience that it also applies to physics and math). You just need to have a free mind and time to experiment on your own, that's all. – Leonid Shifrin May 26 '12 at 16:19
  • 1
    @Andrew We can also replace the first definition of f by f[tree_List] := List@@@Flatten@Apply[lst, f[{}, tree], {-2}];. Now f will work for {423,{53,66,424}} as well as {423, {{53, {39, {66, {67, 81, 423}}, 423}}, {66, {67, 81, 423}}, {424, {25, 40, 423}}}}. – Michael Wijaya Aug 19 '12 at 02:14
  • An unranking algorithm constructs the ith object in an underlying list given rank i. Is there a way to extend your solution to generate the ith transversal of the tree? – M.R. Aug 28 '12 at 17:04
  • @Mike Sorry I don't quite follow the question from your short description. Also, I happen to be short of time in the next day or two. I suggest that you ask a separate follow-up question if this topic is of serious interest to you. I am sure many folks would gladly jump on it. – Leonid Shifrin Aug 28 '12 at 17:11
  • @LeonidShifrin ok thank you! – M.R. Aug 28 '12 at 17:12
  • @LeonidShifrin, why does 'f' simply flatten this modified tree:? {423, {53, {39, {65, 423}, 66}, 424}} – alancalvitti Feb 26 '18 at 18:39
  • @alancalvitti The code above is rather picky in terms of the tree structure. You would need something like this instead: f[{423, {{53, {{39, {65, 423}}}}, {66, {{}}}, {424, {{}}}}}]. – Leonid Shifrin Feb 26 '18 at 19:06
  • @LeonidShifrin, can your function be modified to accept arbitrary nested lists of atomic elements? Or is it worth asking as a separate Q? – alancalvitti Feb 27 '18 at 02:56
  • @alancalvitti I would ask a separate question and link to this one. But note that your question as stated is ill-posed, since arbitrary nested list does not define a tree structure without some additional conventions. For example, it is not clear how to interpret lists like {39, {65, 423}, 66} - what does sublist in the middle mean? If those are child nodes, then what is their parent node? If parent nodes are lists and they have no other identity, then you can't mark intermediate nodes in the traversal, so a set of traversals will be simply equivalent to a set of all atomic elements. – Leonid Shifrin Feb 27 '18 at 13:44
  • @LeonidShifrin, understood, but the FullForm makes it clearer, substituting an implicit "tree" for List in List[39,List[65,423],66] – alancalvitti Feb 27 '18 at 16:25
  • @alancalvitti As I explained, if your nodes are just Lists (that is, you use the natural expression tree structure), they have no identity. So then, how do you mark the traversals? I mean, paths from the root to a particular leaf. You can do that with the positions of the nodes (lists) in parent lists, but that should be then a part of the problem spec. Otherwise, as I said before, each traversal is only characterized by the final leaf, and this kind of kills the purpose. – Leonid Shifrin Feb 27 '18 at 23:51
  • @LeonidShifrin, just append a root node, which is usually the symbol to which the expression is set, eg as = <a->1,b-><|c->3|>,d->4|>. The root is as, same for List. – alancalvitti Feb 28 '18 at 01:01
  • @alancalvitti But this is a different story now, since in nested assoc, you can say that nodes have names (identities), so then the display-all-traversal problem makes sense again. So what is the actual problem you want to solve? All traversals for an arbitrary nested assoc, where anything except assocs inside would be considered leaves? That would make sense. All traversals for a tree built of arbitrary lists does not make sense without additional constraints / info, as I explained above. You need to clearly formulate the problem - in the nested assoc formulation is it at least solvable. – Leonid Shifrin Feb 28 '18 at 01:47
  • What's wrong with this: {List -> 39, List -> List -> 65, List -> List -> 423, List -> 66} using shortcut notation for paths. Might look like cheating to have repeated Heads but nested Keys need not be unique at different levels either. – alancalvitti Feb 28 '18 at 17:26
  • @alancalvitti Nothing is wrong with this, except may be the fact that List does not uniquely identify the node, unlike e.g. node position. Which means that you may not be able to reconstruct the actual traversal from such representation uniquely. But in any case, whatever the convention, it has to be a part of the problem spec. It is up to you how to formulate the problem, my point has been that your original formulation was incomplete. – Leonid Shifrin Feb 28 '18 at 18:25
5

Here is a transformation that will work as well:

Flatten[B //. {
   {x__?NumericQ, {y__?NumericQ}} :> ({x, #} & /@ {y}),
   {x__?NumericQ, {y__List}} :> (Join[{x}, #] & /@ {y})
   }, 1]

yields

{{423, 53, 39}, {423, 53, 65}, {423, 53, 423}, {423, 66, 67}, {423, 
  66, 81}, {423, 66, 423}, {423, 424, 25}, {423, 424, 40}, {423, 424, 
  423}}
M.R.
  • 31,425
  • 8
  • 90
  • 281
  • nice, +1. however this doesn't seem to work generically ; try with {423, {{53, {39, 65, 423}}, {66, {67, 81, 423}}, {424, {25, 40, {4, 3, 4, 2}, 423}}}} – acl May 26 '12 at 14:39
2

Here is another way:

(Thread@{First@B, #[[1]], #[[2]]} & /@ Flatten[Rest@B, 1])~Flatten~1
withparadox2
  • 2,481
  • 1
  • 24
  • 28
  • Seems to break with trees with variable-depth branches: eg try B2 = {423, {{53, {39, 65, 423}}, {66, {67, 81, 423}}, {424, {25, {40, 41}}}}}; – alancalvitti Feb 26 '18 at 18:14