使用恒定空间和 O(n) 运行时间编写二叉搜索树的非递归遍历

发布于 2024-10-28 13:33:40 字数 257 浏览 2 评论 0原文

这不是作业,这是一道面试题。

这里的要点是算法应该是常数空间。 我对如何在没有堆栈的情况下做到这一点一无所知,我会发布我使用堆栈编写的内容,但无论如何它都不相关。

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

任何帮助将不胜感激。

(我将其标记为 Java,因为这是我使用起来很舒服的方式,但很明显,它与语言无关。)

This is not homework, this is an interview question.

The catch here is that the algorithm should be constant space.
I'm pretty clueless on how to do this without a stack, I'd post what I've written using a stack, but it's not relevant anyway.

Here's what I've tried: I attempted to do a pre-order traversal and I got to the left-most node, but I'm stuck there. I don't know how to "recurse" back up without a stack/parent pointer.

Any help would be appreciated.

(I'm tagging it as Java since that's what I'm comfortable using, but it's pretty language agnostic as is apparent.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

岁月打碎记忆 2024-11-04 13:33:40

Morris中序树遍历怎么样?它基于线程树的概念,它修改树,但在完成后将其恢复回来。

链接:http://geeksforgeeks.org/?p=6358

不使用任何额外空间。

How about Morris Inorder tree traversal? Its based on the notion of threaded trees and it modifies the tree, but reverts it back when its done.

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

Doesn't use any extra space.

梦晓ヶ微光ヅ倾城 2024-11-04 13:33:40

我没有完全考虑清楚,但我认为只要你愿意在这个过程中弄乱你的树,这是可能的。

每个 Node 有 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;
    }
  }
}

I didn't think it through entirely, but i think it's possible as long as you're willing to mess up your tree in the process.

Every Node has 2 pointers, so it could be used to represent a doubly-linked list. Suppose you advance from Root to Root.Left=Current. Now Root.Left pointer is useless, so assign it to be Current.Right and proceed to Current.Left. By the time you reach leftmost child, you'll have a linked list with trees hanging off of some nodes. Now iterate over that, repeating the process for every tree you encounter as you go

EDIT: thought it through. Here's the algorithm that prints in-order:

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;
    }
  }
}
舂唻埖巳落 2024-11-04 13:33:40

如果您使用基于向下指针的树并且没有父指针或其他内存,则不可能在常量空间中遍历。

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

If you are using a downwards pointer based tree and don't have a parent pointer or some other memory it is impossible to traverse in constant space.

It is possible if your binary tree is in an array instead of a pointer-based object structure. But then you can access any node directly. Which is a kind of cheating ;-)

苹果你个爱泡泡 2024-11-04 13:33:40

这是 iluxa 的简短版本原始答案
它以完全相同的顺序运行完全相同的节点操作和打印步骤 - 但以简化的方式 [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]
另外,当树根节点没有左子节点时它甚至可以工作。

Here's a shorter version iluxa's original answer.
It runs exactly the same node manipulation and printing steps, in exactly the same order — but in a simplified manner [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]
Plus, it even works when the tree root node has no left child.

草莓味的萝莉 2024-11-04 13:33:40

它是一个搜索树,所以你总是可以得到下一个键/条目

你需要类似的东西(我没有测试代码,但它很简单)

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;
}

It's a a search tree, so you can always get the next key/entry

You need smth like (I didn't test the code, but it's as simple as it gets)

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

for clarity this is higherEntry, so it's not recursive. There you have it :)

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;
}
远山浅 2024-11-04 13:33:40

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

在这种情况下,在纸上画出树的图,然后用铅笔在树周围从边缘的两侧向上和向下绘制(向下时,您将在边缘的左侧,并且上升时,您将位于右侧)。基本上有4种状态:

  1. 西南:您位于边缘的左侧,从父级到左子级
  2. 东北:从左子级返回到其父级
  3. 东南:从父级到右子级
  4. 西北:从右子级返回给它的父级
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;
        }
    }
}

The title of the question doesn't mention the lack of a "parent" pointer in the node. Although it isn't necessarily required for BST, many binary tree implementations do have a parent pointer.
class Node {
Node* left;
Node* right;
Node* parent;
DATA data;
};

It this is the case, imaging a diagram of the tree on paper, and draw with a pencil around the tree, going up and down, from both sides of the edges (when going down, you'll be left of the edge, and when going up, you'll be on the right side). Basically, there are 4 states:

  1. SouthWest: You are on the left side of the edge, going from the parent its left child
  2. NorthEast: Going from a left child, back to its parent
  3. SouthEast: Going from a parent to a right child
  4. NorthWest: Going from a right child, back to its parent
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;
        }
    }
}
浅唱ヾ落雨殇 2024-11-04 13:33:40

我们可以遍历二叉树而不修改树本身(前提是节点有父指针)。并且可以在恒定的空间内完成。我发现这个有用的链接相同
http:// tech.technoflirt.com/2011/03/04/non-recursive-tree-traversal-in-on-using-constant-space/

We can traverse the binary tree without modifying the tree itself (provided nodes have parent pointer). And it can be done in constant space. I found this useful link for the same
http://tech.technoflirt.com/2011/03/04/non-recursive-tree-traversal-in-on-using-constant-space/

十二 2024-11-04 13:33:40

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

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

Accepted answer needs the following change otherwise it will not print the tree where the BST only has one node

if (current == NULL && root != NULL)
   print(root);
对不⑦ 2024-11-04 13:33:40

上面 iluxa 的答案的小特例

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

minor special case for iluxa's answer above

if(current== null)
        {
            current = root;
            parent = current.Right;
            if(parent != null)
            {
                current.Right = parent.Left;
                parent.Left = current;
            }
        }
沫雨熙 2024-11-04 13:33:40

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

It's a binary search tree, so every node can be reached by a series of right/left decision. Describe that series as 0/1, least-significant bit to most-significant. So the function f(0) means "the node found by taking the right-hand branch until you find a leaf; f(1) means take one left and the rest right; f(2) -- that is, binary 010 -- means take a right, then a left, then rights until you find a leaf. Iterate f(n) starting at n=0 until you have hit every leaf. Not efficient (since you have to start at the top of the tree each time) but constant memory and linear time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文