递归与迭代树遍历
Recursive vs Iterative Tree Traversal
所以我在研究树遍历算法。例如,在 K-d 树遍历中,我们的目标是将节点向下遍历到叶子。这与其说是树搜索,不如说是从根到叶的遍历。
在这种情况下,递归解决方案就足够了。但是,在像 C 这样的语言中,递归调用函数需要将值压入堆栈并在堆栈帧之间跳转等。标准递归方法类似于:
void traverse(Node* ptr){
if(ptr.left == null && ptr.right == null) return;
if(ptr.val >= threshold) traverse(ptr.right);
else if(ptr.val < threshold) traverse(ptr.left);
}
traverse(root);
因此,考虑到二叉树有明确的上限(我相信这也可以扩展到其他树类型),迭代执行此遍历是否更有效:
Node* ptr = root;
for(int i = 0; i < tree.maxHeight; i++) {
if (ptr.left == null && ptr.right == null) break;
if (ptr.val >= threshold) ptr = ptr.right;
else if (ptr.val < threshold) ptr = ptr.left
}
二叉树的最大高度是它的节点数,而平衡树的最大高度是 log(n)。因此我想知道迭代解决方案是否有任何缺点,或者它是否确实比普通递归更快。我在这方面缺少什么概念吗?
您的代码与其说是树遍历,不如说是树搜索。如果您想做的只是从根到叶,那么迭代解决方案更简单、更快,并且会使用更少的内存,因为您不必处理堆栈帧。
如果您想要完整遍历树:即 in-order 遍历您访问每个节点的地方,那么您要么编写递归算法,要么实现您自己的堆栈,其中您明确地推送和弹出节点。实现自己的堆栈的迭代方法可能会更快,但您无法避免 O(log n)(在平衡二叉树中)或可能的 O(n)(在退化树中)内存使用。实现显式堆栈将使用较少的内存,仅仅是因为它只需要包含树节点指针,而完整的堆栈框架包含的内存要多得多。
所以我在研究树遍历算法。例如,在 K-d 树遍历中,我们的目标是将节点向下遍历到叶子。这与其说是树搜索,不如说是从根到叶的遍历。
在这种情况下,递归解决方案就足够了。但是,在像 C 这样的语言中,递归调用函数需要将值压入堆栈并在堆栈帧之间跳转等。标准递归方法类似于:
void traverse(Node* ptr){
if(ptr.left == null && ptr.right == null) return;
if(ptr.val >= threshold) traverse(ptr.right);
else if(ptr.val < threshold) traverse(ptr.left);
}
traverse(root);
因此,考虑到二叉树有明确的上限(我相信这也可以扩展到其他树类型),迭代执行此遍历是否更有效:
Node* ptr = root;
for(int i = 0; i < tree.maxHeight; i++) {
if (ptr.left == null && ptr.right == null) break;
if (ptr.val >= threshold) ptr = ptr.right;
else if (ptr.val < threshold) ptr = ptr.left
}
二叉树的最大高度是它的节点数,而平衡树的最大高度是 log(n)。因此我想知道迭代解决方案是否有任何缺点,或者它是否确实比普通递归更快。我在这方面缺少什么概念吗?
您的代码与其说是树遍历,不如说是树搜索。如果您想做的只是从根到叶,那么迭代解决方案更简单、更快,并且会使用更少的内存,因为您不必处理堆栈帧。
如果您想要完整遍历树:即 in-order 遍历您访问每个节点的地方,那么您要么编写递归算法,要么实现您自己的堆栈,其中您明确地推送和弹出节点。实现自己的堆栈的迭代方法可能会更快,但您无法避免 O(log n)(在平衡二叉树中)或可能的 O(n)(在退化树中)内存使用。实现显式堆栈将使用较少的内存,仅仅是因为它只需要包含树节点指针,而完整的堆栈框架包含的内存要多得多。