簡體   English   中英

二叉樹中莫里斯遍歷與遞歸有序的性能

[英]Performance of Morris Traversal vs recursive In-Order in a binary tree

在去面試之前我正在做一些准備工作,我剛剛了解了 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

這是我的 In-Order 方法的代碼:

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");

結果讓我吃驚!

這些是結果:

莫里斯遍歷 TOOK:637 毫秒

遞歸有序 TOOK:367 毫秒

注意 - 當我做這個測試時,我評論了來自 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 毫秒

莫里斯遍歷 TOOK:502786 毫秒

正如您所看到的,與遞歸 In-Order 相比,Morris Traversal 仍然非常慢。 所以我又做了一次測試,只插入了 1000 個節點,每個代碼只運行了 10000 次,結果是:

遞歸有序 TOOK:44 毫秒

莫里斯遍歷 TOOK:97 毫秒

我還是不明白? 為什么莫里斯遍歷速度較慢?

看到這里你必須明白其中的邏輯。 只需對莫里斯進行空運行,順序遍歷就可以說需要時間 2k。 現在為遞歸中序遍歷做空運行,我很確定你會或多或少地找到時間 k。 這里的邏輯是,Morris 中序遍歷所花費的時間是中序遍歷所花費的時間的兩倍或多或少,因為我們最多到達每個節點 2 次。 一次用於創建虛擬右鏈接,另一個用於刪除該正確鏈接。

現在,如果您對空間復雜度感興趣,那么莫里斯中序遍歷就像是一個國王。 在這里你不需要額外的空間。 它的空間復雜度是 O(1),但是遞歸中序遍歷的空間復雜度是 O(n)。 希望現在你可以理解這兩種遍歷策略的用途了。 :)

如果不將性能估計分解為它們的組件,很難判斷,但是當用假的正確部分回溯節點時,Morris Traversal 基本上遍歷了樹的一部分。 您還在莫里斯循環內做更多的工作,因此常數因子會更大。 奇怪的是它幾乎是 2 倍,但如果沒有更多細節,我不會依賴於實際成本。

你沒有熱身通行證。 您應該先執行這兩種方法,然后再定時執行它們。 您還忽略了大多數 Java 代碼在長時間運行的進程中運行的事實,這些進程最終會編譯為機器代碼。 我會嘗試使用較小的樹並多次執行遍歷。

Morris Traversal -- O(2n)-> O(n) 時間 有序方法 -- O(2n)-> O(n) 時間

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM