使用常量空间和O(n)运行时编写二进制搜索树的非递归遍历

时间:2011-03-31 07:19:40

标签: java binary-tree traversal tree-traversal

这不是家庭作业,这是一个面试问题。

这里的问题是算法应该是恒定的空间。 我对如何在没有堆栈的情况下做到这一点非常无能为力,我会发布我使用堆栈编写的内容,但无论如何它都不相关。

这是我尝试过的:我试图进行预订遍历,然后我到了最左边的节点,但是我被困在那里。我不知道在没有堆栈/父指针的情况下如何“递归”备份。

任何帮助都将不胜感激。

(我将它标记为Java,因为这是我很习惯使用的,但它显然是语言无关的。)

10 个答案:

答案 0 :(得分:30)

我并没有完全认为它,但我认为只要你愿意在这个过程中弄乱你的树,就有可能。

每个节点都有2个指针,因此它可以用来表示双向链表。假设你从Root前进到Root.Left = Current。现在Root.Left指针是无用的,所以将它指定为Current.Right并继续到Current.Left。当你到达最左边的孩子时,你会有一个链表,树上挂着一些节点。现在迭代一遍,为你去的每一棵树重复这个过程

编辑:想一想。这是按顺序打印的算法:

void traverse (Node root) {
  traverse (root.left, root);
}

void traverse (Node current, Node parent) {
  while (current != null) {
    if (parent != null) {
      parent.left = current.right;
      current.right = parent;
    }

    if (current.left != null) {
      parent = current;
      current = current.left;
    } else {
      print(current);
      current = current.right;
      parent = null;
    }
  }
}

答案 1 :(得分:28)

Morris Inorder树遍历怎么样?它基于线程树的概念,它修改树,但在完成后将其还原。

Linkie:http://geeksforgeeks.org/?p=6358

不使用任何额外空间。

答案 2 :(得分:4)

如果您使用的是基于向下指针的树,并且没有父指针或其他内存,则无法在恒定空间中遍历。

如果您的二叉树是数组而不是基于指针的对象结构,则可能。但是,您可以直接访问任何节点。这是一种作弊行为; - )

答案 3 :(得分:3)

这是iluxa original answer的缩短版。 它以完全相同的顺序运行完全相同的节点操作和打印步骤 - 但是以简化的方式[1]:

void traverse (Node n) {
  while (n) {
    Node next = n.left;
    if (next) {
      n.left = next.right;
      next.right = n;
      n = next;
    } else {
      print(n);
      n = n.right;
    }
  }
}

[1] 此外,它甚至可以在树根节点没有左子节点时工作。

答案 4 :(得分:0)

这是一个搜索树,因此您可以随时获取下一个键/条目

你需要smth(我没有测试代码,但它很简单)

java.util.NavigableMap<K, V> map=...
for (Entry<K, V> e = map.firstEntry(); e!=null; e = map.higherEntry(e.getKey())) {
  process(e)
}

为清楚起见,这是higherEntry,所以它不是递归的。你有它:))

final Entry<K,V> getHigherEntry(K key) {
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp < 0) {
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else {
            if (p.right != null) {
                p = p.right;
            } else {
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}

答案 5 :(得分:0)

问题的标题没有提到节点中缺少“父”指针。虽然BST不一定需要它,但许多二叉树实现确实有父指针。     class Node {         节点*离开;         节点*权利;         节点*父节点;         数据数据;     };

就是这种情况,在树上画一张树的图,然后用一支铅笔在树周围画画,从边缘的两边上下移动(当你走下去时,你将被留下边缘,当上升时,你将在右侧)。基本上,有4种状态:     

        
  1. SouthWest:您位于边缘的左侧,从父亲的左侧孩子开始
  2.     
  3. NorthEast:从左孩子回到父母
  4.     
  5. SouthEast:从父母到正确的孩子
  6.     
  7. NorthWest:从一个正确的孩子,回到其父母
  8.     

    Traverse( Node* node )
    {
        enum DIRECTION {SW, NE, SE, NW};
        DIRECTION direction=SW;
    
        while( node )
        {
            // first, output the node data, if I'm on my way down:
            if( direction==SE or direction==SW ) {
                out_stream << node->data;
            }
    
            switch( direction ) {
            case SW:                
                if( node->left ) {
                    // if we have a left child, keep going down left
                    node = node->left;
                }
                else if( node->right ) {
                    // we don't have a left child, go right
                    node = node->right;
                    DIRECTION = SE;
                }
                else {
                    // no children, go up.
                    DIRECTION = NE;
                }
                break;
            case SE:
                if( node->left ) {
                    DIRECTION = SW;
                    node = node->left;
                }
                else if( node->right ) {
                    node = node->right;
                }
                else {
                    DIRECTION = NW;
                }
                break;
            case NE:
                if( node->right ) {
                    // take a u-turn back to the right node
                    node = node->right;
                    DIRECTION = SE;
                }
                else {
                    node = node->parent;
                }
                break;
            case NW:
                node = node->parent;
                break;
            }
        }
    }
    

答案 6 :(得分:0)

接受的答案需要以下更改,否则不会打印BST只有一个节点的树

if (current == NULL && root != NULL)
   print(root);

答案 7 :(得分:0)

上述iluxa答案的小特例

if(current== null)
        {
            current = root;
            parent = current.Right;
            if(parent != null)
            {
                current.Right = parent.Left;
                parent.Left = current;
            }
        }

答案 8 :(得分:-1)

这是一个二进制搜索树,因此可以通过一系列右/左决策来访问每个节点。将该系列描述为0/1,最低有效位为最重要。所以函数f(0)的意思是“通过右手分支找到的节点,直到找到一个叶子; f(1)表示左边一个,右边是右边; f(2) - 即二进制010 - - 意味着取右,然后是左,然后是权利,直到你找到一个叶子。迭代f(n)从n = 0开始直到你击中每一片叶子。效率不高(因为你必须从树的顶部开始时间)但恒定的记忆和线性时间。

答案 9 :(得分:-1)

我们可以在不修改树本身的情况下遍历二叉树(假设节点有父指针)。它可以在恒定的空间内完成。我发现了这个有用的链接 http://tech.technoflirt.com/2011/03/04/non-recursive-tree-traversal-in-on-using-constant-space/