Iterating over a Binary Tree with O(1) Auxiliary Space

后端 未结 10 1253
广开言路
广开言路 2020-11-30 04:03

Is it possible to iterate over a binary tree in O(1) auxiliary space (w/o using a stack, queue, etc.), or has this been proven impossible? If it is possible, how can it be

相关标签:
10条回答
  • 2020-11-30 04:35

    Geez, I'll have to actually type it up from Knuth. This solution is by Joseph M. Morris [Inf. Proc. Letters 9 (1979), 197-200]. As far as I can tell, it runs in O(NlogN) time.

    static void VisitInO1Memory (Node root, Action<Node> preorderVisitor) {
      Node parent = root ;
      Node right  = null ;
      Node curr ;
      while (parent != null) {
        curr = parent.left ;
        if (curr != null) {
          // search for thread
          while (curr != right && curr.right != null)
            curr = curr.right ;
    
          if (curr != right) {
            // insert thread
            assert curr.right == null ;
            curr.right = parent ;
            preorderVisitor (parent) ;
            parent = parent.left ;
            continue ;
          } else
            // remove thread, left subtree of P already traversed
            // this restores the node to original state
            curr.right = null ;
        } else
          preorderVisitor (parent) ;
    
        right  = parent ;
        parent = parent.right ;
      }
    }
    
    class Node
    {
      public Node left  ;
      public Node right ;
    }
    
    0 讨论(0)
  • 2020-11-30 04:36

    You can do it destructively, unlinking every leaf as you go. This may be applicable in certain situations, i.e. when you don't need the tree afterwards anymore.

    By extension, you could build another binary tree as you destroy the first one. You would need some memory micromanagement to make sure that peak memory never exceeds the size of the original tree plus perhaps a little constant. This would likely have quite some computing overhead, though.

    EDIT: There is a way! You can use the nodes themselves to light the way back up the tree by temporarily reversing them. As you visit a node, you point its left-child pointer to its parent, its right-child pointer to the last time you took a right turn on your path (which is to be found in the parent's right-child pointer at this moment), and store its real children either in the now-redundant parent's right-child pointer or in your traversal state resp. the next visited children's left-child pointer. You need to keep some pointers to the current node and its vicinity, but nothing "non-local". As you get back up the tree, you reverse the process.

    I hope I could make this somehow clear; this is just a rough sketch. You'll have to look it up somewhere (I am sure that this is mentioned somewhere in The Art of Computer Programming).

    0 讨论(0)
  • 2020-11-30 04:38

    Pointers from nodes to their ancestors can be had with no (well, two bits per node) additional storage using a structure called a threaded tree. In a threaded tree, null links are represented by a bit of state rather than a null pointer. Then, you can replace the null links with pointers to other nodes: left links point to the successor node in an inorder traversal, and right links to the predecessor. Here is a Unicode-heavy diagram (X represents a header node used to control the tree):

                                             ╭─┬────────────────────────────────────────╮
       ╭─────────────────────────▶┏━━━┯━━━┯━━▼┓│                                        │
       │                        ╭─╂─  │ X │  ─╂╯                                        │ 
       │                        ▼ ┗━━━┷━━━┷━━━┛                                         │
       │                    ┏━━━┯━━━┯━━━┓                                               │
       │               ╭────╂─  │ A │  ─╂──╮                                            │
       │               ▼    ┗━━━┷━━━┷━━━┛  │                                            │    
       │        ┏━━━┯━━━┯━━━┓    ▲         │        ┏━━━┯━━━┯━━━┓                       │
       │      ╭─╂─  │ B │  ─╂────┤         ├────────╂─  │ C │  ─╂───────╮               │
       │      ▼ ┗━━━┷━━━┷━━━┛    │         ▼        ┗━━━┷━━━┷━━━┛       ▼               │  
       │┏━━━┯━━━┯━━━┓ ▲          │   ┏━━━┯━━━┯━━━┓       ▲         ┏━━━┯━━━┯━━━┓        │
       ╰╂─  │ D │  ─╂─╯          ╰───╂   │ E │  ─╂╮      │        ╭╂─  │ F │  ─╂╮       │ 
        ┗━━━┷━━━┷━━━┛                ┗━━━┷━━━┷━━━┛▼      │        ▼┗━━━┷━━━┷━━━┛▼       │
                                        ▲ ┏━━━┯━━━┯━━━┓  │ ┏━━━┯━━━┯━━━┓ ▲ ┏━━━┯━━━┯━━━┓│
                                        ╰─╂─  │ G │   ╂──┴─╂─  │ H │  ─╂─┴─╂─  │ J │  ─╂╯
                                          ┗━━━┷━━━┷━━━┛    ┗━━━┷━━━┷━━━┛   ┗━━━┷━━━┷━━━┛
    
    

    Once you have the structure, doing an inorder traversal is very, very easy:

    Inorder-Successor(p)
        p points to a node.  This routine finds the successor of p in
        an inorder traversal and returns a pointer to that node
    
        qp.right
        If p.rtag = 0 Then
            While q.ltag = 0 Do
                qq.left
            End While
        End If
    
        Return q
        

    Lots more information on threaded trees can be found in Art of Computer Programming Ch.2 §3.1 or scattered around the Internet.

    0 讨论(0)
  • 2020-11-30 04:41

    I think there's no way you could do this, as you should somehow find the nodes where you left off in a path and to identify that you always need O(height) space.

    0 讨论(0)
  • 2020-11-30 04:41

    Is it possible to iterate over a binary tree in O(1) auxiliary space.

    struct node { node * father, * right, * left; int value; };
    

    This structure will make you be able to move 1-step in any direction through the binary tree.
    But still in iteration you need to keep the path!

    0 讨论(0)
  • 2020-11-30 04:42

    To preserve the tree and only use O(1) space it's possible if...

    • Each node is a fixed size.
    • The whole tree is in a contiguous part of memory (i.e. an array)
    • You don't iterate over the tree, you simply iterate over the array

    Or if you destroy the tree as you process it...:

    • @Svante came up with this idea, but I wanted to expand on how a little, using a destructive approach.
    • How? You can continue to take the left most leaf node in a tree (for(;;) node = node->left etc... , then process it, then delete it. If the leftmost node in the tree is not a leaf node, then you process and delete the right node's left most leaf node. If a right node has no children, then you process and delete it.

    Ways that it wouldn't work...

    If you use recursion you will use a stack implicitly. For some algorithms (not for this problem) tail recursion would allow you to use recursion and have O(1) space, but since any particular node may have several children, and hence there is work to do after the recursive call, O(1) space tail recursion is not possible.

    You could try to tackle the problem 1 level at a time, but there is no way to access an arbitrary level's nodes without auxiliary (implicit or explicit) space. For example you could recurse to find the node you want, but then that takes implicit stack space. Or you could store all your nodes in another data structure per level, but that takes extra space too.

    0 讨论(0)
提交回复
热议问题