在遍歷兒叉樹時,常常使用的是遞歸遍歷,或者是借助於棧來迭代,在遍歷過程中,每個節點僅訪問一次,所以這樣遍歷的時間復雜度為O(n),空間復雜度為O(n),並且遞歸的算法易於理解和實現。在遍歷過程中,遞歸遍歷過程的空間復雜度卻是O(n),就算是轉換為使用棧空間迭代時間,還是沒有改變算法對額外空間的需求,在學習數據結構課程時,還學習了線索二叉樹,在線索二叉樹中,使用線索來保存節點的前驅和后繼的信息,而這些線索是利用了葉節點的空指針域來保存,所以知道了樹種每個節點的前驅和后繼的位置(指針)可以有效降低遍歷過程中對空間的需求,但是使用線索二叉樹必須先通過一次二叉樹遍歷算法,為二叉樹建立線索,此外還需要標記每個節點的指針域是線索還是指針,使每個結點都有了唯一前驅和后繼(第一個結點無前驅,最后一個結點無后繼)。對於二叉樹的一個結點,查找其左右子女是方便的,其前驅后繼只有在遍歷中得到。為了容易找到前驅和后繼,有兩種方法。一是在結點結構中增加向前和向后的指針fwd和bkd,這種方法增加了存儲開銷,不可取;二是利用二叉樹的空鏈指針。現將二叉樹的結點結構重新定義如下:
lchild
|
ltag
|
data
|
rtag
|
rchild
|
下面來看看Morris二叉樹遍歷算法。
Morris算法與遞歸和使用棧空間遍歷的思想不同,它使用二叉樹中的葉節點的right指針來保存后面將要訪問的節點的信息,當這個right指針使用完成之后,再將它置為NULL,但是在訪問過程中有些節點會訪問兩次,所以與遞歸的空間換時間的思路不同,Morris則是使用時間換空間的思想,這里講的很簡單,大體意思每次去找當前節點的左子樹的最右子節點,最右子節點的右節點指向當前根節點,然后以左子樹為根節點,再找其左子樹的最右子節點,最右子節點再指向當前的根節點。這是我自己的理解,以中序遍歷為例。下面是代碼實現:
public static void morrisIn(Node head){ if (head==null){ return; } Node cur1=head; Node cur2=null; while(cur1 !=null){ cur2=cur1.left; if(cur2!=null){ while (cur2.right !=null && cur2.right!=cur1){ cur2=cur2.right; } if(cur2.right ==null ){ cur2.right =cur1; cur1 = cur1.left; continue; }else { cur2,right=null; } } Sysetem.out.print(cur1.value+" "); cur1=cur1.right; } System.out.println(); }
前序遍歷就是第一次訪問的該節點是就答應該節點,代碼示例略。
后序遍歷比較復雜,即在訪問該節點的左子樹的最右子節點時,最右子節點的子節點之前為空,由於現在指向着該節點,即打印時機為第二次訪問到一個根節點是,倒序打印該根節點的左子樹的整個最右子樹。程序差不都,掌握打印時機即可。(再加一個倒序打印左子樹的整個最右子樹函數即可),代碼貼圖如下: