6

Given a binary tree with an integer, Left & Right pointers, how can one traverse the tree in O(n) time and O(1) extra memory (no stack/queue/recursion)?

This guy gave a solution which is not O(n) total time that encoded the current path as an integer (and thus works on for trees of limited depth).

I am looking for the classical solution

(SPOILER)

that encoded the parent of each node in the children.

ripper234
  • 222,824
  • 274
  • 634
  • 905

5 Answers5

6

Any good algorithm book will have this algorithm, look e.g. in Knuth (TAOCP I.2.3.1 Traversing binary trees, excercise 21). However, because this algorithm modifies the tree in place, you must use extreme caution in a multi-threaded environment.

You might also use threaded trees (see in Knuth).

Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • Er, never mind, he claims O(n) but he's revisiting the parents. – Jeremy Stanley Apr 05 '09 at 10:05
  • 1
    Here it is: http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1226699256 (I think) – Jeremy Stanley Apr 05 '09 at 10:10
  • @jeremy, nope, that doesn't work either, that poster is writing data into the node. – SPWorley Apr 30 '09 at 23:34
  • I typed up the algorithm from TAOCP here http://stackoverflow.com/questions/791052/iterating-over-a-binary-tree-with-o1-auxiliary-space It also writes data into the node, but restores the node to previous state afterwards. – Anton Tykhyy May 01 '09 at 07:38
3

The main idea is similar to the list inversion algorithm, with one super-ugly tricky hack (from a theoretical point of view, probably a cheat), based on the fact that pointers are (in all langugae currently known to humans), 0 mode 4 as integers.

The idea is that you can flip the pointers on the path down the tree to point upwards. The problem is that - and this is where you divert from the list inversion algorithm - when you backtrack you need to know if left points up or right points up; at which point we use the hack.

Pseudo code follows:

current = root->left
next = current
while (current != null) {
  next = current->left
  current->left = static_cast<int>(prev) + 1 // ugly hack.
  current = next
}
status = done
while (current != root or status != done) {
  if (status = done) {
     if (static_cast<u32>(current->left) %4 = 1) {
         next = static_cast<u32>(current->left) -1
         current->left = prev
         status = middle
     }
     else {
         next = current->right
         current->right = prev
         status = done
     }
     prev = current
     current = next
  }
  else if (status == left) {
     if (current->left) {
       prev = current->left
       current->left = static_cast<u32>(next) +1
       next = current
     }
     else
       status = middle
  }
  else if (status == right) {
     if (current->right) {
        prev = current->right;
        current ->right = next;
        next = current
     }
     else
       status = done
  }
  else {// status == middle
     work_on_node(current)
     status = right
  }
}
David Lehavi
  • 1,186
  • 7
  • 16
1

That guy's algorithm is interesting, but it needs to be pointed out that it does require O(log n) extra bits of space to traverse a binary tree with n nodes. Space requirements must be measured in bits, not bytes -- usually they collapse into the same thing when Big Oh notation is used, but cases like this point out why it's important to make the distinction.

To see this, ask how a tree with more than 2^32-1 nodes can be traversed using a single integer of storage (on a 32-bit platform).

j_random_hacker
  • 50,331
  • 10
  • 105
  • 169
  • Nobody said the tree is balanced. The tree could be a string of 200 nodes and the algorithm would fail on it. – ripper234 Apr 06 '09 at 12:04
  • @ripper234: OK... I was just making the point that, even in the best (perfectly balanced) case for the algorithm, it still requires more than O(1) space. Yes, the more unbalanced the tree, the closer you get to O(n) space, and the more quickly you'll exhaust the bits in a single integer. – j_random_hacker Apr 06 '09 at 15:16
0

Here is another solution

http://sites.google.com/site/debforit/efficient-binary-tree-traversal-with-two-pointers

But I was wondering if there is a way to do this in languages like Java which DO NOT have pointers in true sense.

Deep
  • 5,772
  • 2
  • 26
  • 36
  • With Java, the way to refer to objects is with references. So, perhaps abusing terminology a bit, in Java you have nothing BUT pointers. – Cannoliopsida Nov 07 '13 at 19:20
0

Use O(1) storage to remember a "last node visited" pointer. You can initialize it to 0 or some unknown value.

To walk the tree, start at the root node. Look at both of the node's children. If your "last visited node" is equal to the RIGHT node, then step to the parent node. If the "last visited" is equal to the LEFT node, then step to the right node. Else step to the left node.

Repeat until you've finished walking the whole tree. The only real cleverness is using the one variable to remember where you're coming from in order to decide where to go next. This makes the traversal deterministic.

You'll end up taking O(n) steps. You'll visit every middle node three times, and every leaf once, so you're still O(N). Storage is O(1).

SPWorley
  • 11,550
  • 9
  • 43
  • 63
  • How do you step to the parent node: you only have left and right pointer? – Nicolas Apr 06 '09 at 12:30
  • And who said "one variable"? If you need to use to use ten variables, it's still O(1) storage. ;-) – peSHIr Apr 07 '09 at 06:23
  • This doesn't work. You have to store the "last node visited" somewhere before you go to visit a different node, so that you know what you visited last when you come back to it. – Anton Tykhyy Apr 30 '09 at 08:54