Morris Traversal的性能与二叉树中的递归有序

时间:2013-02-06 00:00:24

标签: java performance binary-tree tree-traversal inorder

我在接受采访前做了一些准备工作,我刚刚了解了Morris Traversal。

这是我用Java编写的Morris Traversal代码(其工作):

protected void morrisTraversal(){
    BinaryNode pre = null;//BinaryNode is a class which represent a node in the tree
    BinaryNode current = this.root;//root is the root of the tree
    while(current != null){
        if(current.getLeftChild() == null){
            System.out.println(current.getNodeData().getId());//id is unique
            current = current.getRightChild();
        }//if
        else{
            pre = current.getLeftChild();
            while(pre.getRightChild() != null && pre.getRightChild().getNodeData().getId() != current.getNodeData().getId())
                pre = pre.getRightChild();
            if(pre.getRightChild() == null){
                pre.setRightChild(current);
                current = current.getLeftChild();
            }//if
            else{
                System.out.println(current.getNodeData().getId());
                current = current.getRightChild();
                pre.setRightChild(null);
            }//else
        }//else
    }//while
}//MorrisTraversal

这是我的有序方法代码:

protected void recInOrder() {
    if(this.root != null)
        recInOrderHelper(this.root);
    else
        System.out.println("The Tree Is Empty");;
}//recInOrder


private void recInOrderHelper(BinaryNode node)  {
    if(node != null){
        recInOrderHelper(node.getLeftChild());
        System.out.println(node.getNodeData().getId());
        recInOrderHelper(node.getRightChild());
    }
}//recInOrderHelper

我在主要做的是: 我插入了我的二叉树70,000,000个节点,然后我做了下一个小检查:

long start = System.currentTimeMillis();
tree4.morrisTraversalInOrder();
System.out.println("morris traversal TOOK: " + (System.currentTimeMillis() - start) + " ms");

start = System.currentTimeMillis();
tree4.recInOrder();
System.out.println("recursive in-order TOOK: " + (System.currentTimeMillis() - start) + " ms");

结果让我感到惊讶!

结果如下:

Morris Traversal TOOK:637 ms

递归有序TOOK:367 ms

注意 - 当我做这个测试时,我评论了来自morrisTraversal()和recInOrderHelper()方法的所有System.out.println(...)。

这些结果让我感到惊讶,因为我认为Morris Traversal会更快,因为它的迭代(不打开堆栈帧等)和In-Order是递归的(每次递归调用打开大约70,000,000个堆栈帧)

我知道他们俩都是O(n), 所以很明显我错了一些事情,但是哪个?我错过了什么?为什么Morris Traversal比递归In-Order慢得多?

编辑 - 我也做了下一次测试: 而不是插入70,000,000个节点到树我插入100,000个节点 我确实运行了下一个代码:

//running the two algorithms one time before doing the actual test(but in the same execution)
tree4.recInOrder();
tree4.morrisTraversalInOrder();

//starting the actual test
long start = System.currentTimeMillis();
for(i = 0; i < 100000; i++){
    tree4.recInOrder(); 
}
System.out.println("Recursive In-Order TOOK: " + (System.currentTimeMillis() - start) + " ms");

start = System.currentTimeMillis();
for(i = 0; i < 100000; i++){
    tree4.morrisTraversalInOrder(); 
}
System.out.println("Morris Traversal TOOK: " + (System.currentTimeMillis() - start) + " ms");

结果是:

递归有序TOOK:214434 ms

Morris Traversal TOOK:502786 ms

正如您所看到的,与递归In-Order相比,Morris Traversal仍然非常缓慢。 所以我做了另一个测试,只插入了1,000个节点,每个代码只运行了10,000次 结果是:

递归有序TOOK:44 ms

Morris Traversal TOOK:97 ms

我还是不明白吗?为什么Morris Traversal比较慢?

3 个答案:

答案 0 :(得分:1)

请参阅此处,您必须了解逻辑。只需对morris进行空运行以遍历遍历,可以说需要2k的时间。现在对递归有序遍历进行空运行,我非常确定您会发现k或多或少的时间。 这里的逻辑是,莫里斯有序遍历所花费的时间大约是有序遍历所花费时间的两倍,因为我们最多到达每个节点2次。一次用于创建虚拟右链接,另一次用于删除该右链接。

现在,如果您对空间复杂性感兴趣,那么莫里斯有序遍历就像国王一样。在这里,您不需要多余的空间。它的空间复杂度是O(1),但是递归有序遍历的空间复杂度是O(n)。 希望现在,您可以了解这两种遍历策略的用法。 :)

答案 1 :(得分:0)

如果不将性能估计分解为其组件,很难说,但是当使用假的右侧部分追溯节点时,Morris Traversal基本上遍历了树的一部分。你也在Morris循环中做更多的工作,所以常数因子会更大。奇怪的是它几乎是2倍,但如果没有进一步的细节,我不会依赖于实际成本。

答案 2 :(得分:0)

你没有热身传球。您应该首先执行这两种方法,然后再次执行它们。您也忽略了大多数Java代码在长时间运行的进程中运行的事实,这些进程最终会编译为机器代码。我会尝试使用较小的树并多次执行遍历。