用于制作二叉树的递归算法的迭代版本
鉴于这个算法,我想知道是否存在迭代版本。 另外,我想知道迭代版本是否可以更快。
这是某种伪Python......
算法返回对树根的引用
make_tree(array a)
if len(a) == 0
return None;
node = pick a random point from the array
calculate distances of the point against the others
calculate median of such distances
node.left = make_tree(subset of the array, such that the distance of points is lower to the median of distances)
node.right = make_tree(subset, such the distance is greater or equal to the median)
return node
Given this algorithm, I would like to know if there exists an iterative version. Also, I want to know if the iterative version can be faster.
This some kind of pseudo-python...
the algorithm returns a reference to root of the tree
make_tree(array a)
if len(a) == 0
return None;
node = pick a random point from the array
calculate distances of the point against the others
calculate median of such distances
node.left = make_tree(subset of the array, such that the distance of points is lower to the median of distances)
node.right = make_tree(subset, such the distance is greater or equal to the median)
return node
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
一个只有一次递归调用的递归函数通常不需要太多努力就可以变成尾递归函数,然后将其转换为迭代函数也很简单。 这里的典型示例是阶乘:
但是,这里的情况涉及两个递归调用,除非您显着修改算法,否则您需要保留一个堆栈。 管理自己的堆栈可能比使用 Python 的函数调用堆栈快一点,但增加的速度和深度可能不值得付出如此复杂的代价。 这里的典型示例是斐波那契数列:
现在,您的情况比这要困难得多:一个简单的累加器将难以用指向需要生成子树的指针来表达部分构建的树。 你需要一个zipper——用非真正功能性的语言来实现并不容易,比如Python。
A recursive function with only one recursive call can usually be turned into a tail-recursive function without too much effort, and then it's trivial to convert it into an iterative function. The canonical example here is factorial:
However, your case here involves two recursive calls, and unless you significantly rework your algorithm, you need to keep a stack. Managing your own stack may be a little faster than using Python's function call stack, but the added speed and depth will probably not be worth the complexity. The canonical example here would be the Fibonacci sequence:
Now, your case is a lot tougher than this: a simple accumulator will have difficulties expressing a partly-built tree with a pointer to where a subtree needs to be generated. You'll want a zipper -- not easy to implement in a not-really-functional language like Python.
制作迭代版本只需使用您自己的堆栈而不是普通的语言调用堆栈即可。 我怀疑迭代版本会更快,因为普通调用堆栈已为此目的进行了优化。
Making an iterative version is simply a matter of using your own stack instead of the normal language call stack. I doubt the iterative version would be faster, as the normal call stack is optimized for this purpose.
您获得的数据是随机的,因此树可以是任意二叉树。 对于这种情况,您可以使用线程二叉树,它可以遍历和构建,无需递归,也无需堆栈。 这些节点有一个标志,指示该链接是否是到另一个节点的链接或如何到达“下一个节点”。
来自 http://en.wikipedia.org/wiki/Threaded_binary_tree
The data you're getting is random so the tree can be an arbitrary binary tree. For this case, you can use a threaded binary tree, which can be traversed and built w/o recursion and no stack. The nodes have a flag that indicate if the link is a link to another node or how to get to the "next node".
From http://en.wikipedia.org/wiki/Threaded_binary_tree
根据您如何定义“迭代”,还有前面的答案未提及的另一个解决方案。 如果“迭代”仅意味着“不受堆栈溢出异常影响”(但“允许使用‘let rec’”),那么在支持尾部调用的语言中,您可以使用延续(而不是“显式”堆”)。 下面的 F# 代码说明了这一点。 它与您原来的问题类似,因为它从数组中构建了 BST。 如果数组随机打乱,则树相对平衡,并且递归版本不会创建太深的堆栈。 但是关闭洗牌,树就会变得不平衡,递归版本堆栈溢出,而迭代与延续版本则愉快地继续前进。
Depending on how you define "iterative", there is another solution not mentioned by the previous answers. If "iterative" just means "not subject to a stack overflow exception" (but "allowed to use 'let rec'"), then in a language that supports tail calls, you can write a version using continuations (rather than an "explicit stack"). The F# code below illustrates this. It is similar to your original problem, in that it builds a BST out of an array. If the array is shuffled randomly, the tree is relatively balanced and the recursive version does not create too deep a stack. But turn off shuffling, and the tree gets unbalanced, and the recursive version stack-overflows whereas the iterative-with-continuations version continues along happily.
是的,可以使任何递归算法迭代。 隐式地,当您创建递归算法时,每次调用都会将先前的调用放入堆栈中。 您想要做的是将隐式调用堆栈变成显式调用堆栈。 迭代版本不一定会更快,但您不必担心堆栈溢出。 (在我的答案中使用网站名称会获得徽章吗?
Yes it is possible to make any recursive algorithm iterative. Implicitly, when you create a recursive algorithm each call places the prior call onto the stack. What you want to do is make the implicit call stack into an explicit one. The iterative version won't necessarily be faster, but you won't have to worry about a stack overflow. (do I get a badge for using the name of the site in my answer?
虽然从一般意义上讲,直接将递归算法转换为迭代算法确实需要显式堆栈,但有一个特定的算法子集可以直接以迭代形式呈现(不需要堆栈)。 这些渲染可能没有相同的性能保证(迭代功能列表与递归解构),但它们确实经常存在。
While it is true in the general sense that directly converting a recursive algorithm into an iterative one will require an explicit stack, there is a specific sub-set of algorithms which render directly in iterative form (without the need for a stack). These renderings may not have the same performance guarantees (iterating over a functional list vs recursive deconstruction), but they do often exist.
这是基于堆栈的迭代解决方案(Java):
Here is stack based iterative solution (Java):