我在接受采访前做了一些准备工作,我刚刚了解了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比较慢?
答案 0 :(得分:1)
请参阅此处,您必须了解逻辑。只需对morris进行空运行以遍历遍历,可以说需要2k的时间。现在对递归有序遍历进行空运行,我非常确定您会发现k或多或少的时间。 这里的逻辑是,莫里斯有序遍历所花费的时间大约是有序遍历所花费时间的两倍,因为我们最多到达每个节点2次。一次用于创建虚拟右链接,另一次用于删除该右链接。
现在,如果您对空间复杂性感兴趣,那么莫里斯有序遍历就像国王一样。在这里,您不需要多余的空间。它的空间复杂度是O(1),但是递归有序遍历的空间复杂度是O(n)。 希望现在,您可以了解这两种遍历策略的用法。 :)
答案 1 :(得分:0)
如果不将性能估计分解为其组件,很难说,但是当使用假的右侧部分追溯节点时,Morris Traversal基本上遍历了树的一部分。你也在Morris循环中做更多的工作,所以常数因子会更大。奇怪的是它几乎是2倍,但如果没有进一步的细节,我不会依赖于实际成本。
答案 2 :(得分:0)
你没有热身传球。您应该首先执行这两种方法,然后再次执行它们。您也忽略了大多数Java代码在长时间运行的进程中运行的事实,这些进程最终会编译为机器代码。我会尝试使用较小的树并多次执行遍历。