9

I want to traverse the following tree structure tail recursively without falling back on loops:

const o = {x:0,c:[{x:1,c:[{x:2,c:[{x:3},{x:4,c:[{x:5}]},{x:6}]},{x:7},{x:8}]},{x:9}]};

        0
       / \
      1   9
    / | \ 
   2  7  8
 / | \
3  4  6
   |
   5

The desired result: /0/1/2/3/4/5/6/7/8/9

I guess a closure is required to enable tail recursion. I've tried this so far:

const traverse = o => {
  const nextDepth = (o, index, acc) => {
    const nextBreadth = () => o["c"] && o["c"][index + 1]
     ? nextDepth(o["c"][index + 1], index + 1, acc)
     : acc;

    acc = o["c"]
     ? nextDepth(o["c"][0], index, acc + "/" + o["x"]) // not in tail pos
     : acc + "/" + o["x"];

    return nextBreadth();
  };

  return nextDepth(o, 0, "");
};

traverse(o); // /0/1/2/3/4/5/7/9

The siblings are not traversed properly. How can this be done?

  • http://codereview.stackexchange.com/questions/47932/recursion-vs-iteration-of-tree-structure –  Jul 07 '16 at 07:09
  • 2
    You cannot traverse a tree using only tailrecursion unless you want to manually maintain a stack. – Bergi Jul 07 '16 at 07:13
  • 1
    How would you write it with loops? Try that first, then convert the loop into a tailrecursive function. – Bergi Jul 07 '16 at 07:14
  • @Bergi What a pity! I tried this for hours without any success. I thought maybe with closures handling `index` as a free variable could be a viable option. That would be probably the mimicked stack you speak of. –  Jul 07 '16 at 07:21
  • @Bergi are you sure it's not possible? What if you recurse over the "levels" of the tree (selecting all relevant children to send in to the recursion for the next level), instead of recursing over individual nodes? – Magne Apr 02 '22 at 08:56
  • @Magne Then you've built a breadth-first search with a queue. OP wants a depth-first search. – Bergi Apr 02 '22 at 17:09

1 Answers1

10

As @Bergi wrote if you manually maintain stack the solution is straightforward.

const o = {x:0,c:[{x:1,c:[{x:2,c:[{x:3},{x:4,c:[{x:5}]},{x:6}]},{x:7},{x:8}]},{x:9}]}

const traverse = g => {
  const dfs = (stack, head) => (head.c || []).concat(stack)
  
  const loop = (acc, stack) => {
    if (stack.length === 0) {
     return acc
    }

    const [head, ...tail] = stack
    return loop(`${acc}/${head.x}`, dfs(tail, head))
  }
  
  return loop('', [g])
}

console.log(traverse(o))
console.log(traverse(o) === '/0/1/2/3/4/5/6/7/8/9')
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • Very nice, full tail recursive solution, thanks! Handling your own stack doesn't seem to be so hard. –  Jul 07 '16 at 08:28
  • 2
    One of the best question/answers I've seen on SO in a very long time. – Mulan Jul 08 '16 at 00:09