經典演算法 Morris遍歷

2022-09-07 13:12:09 字數 4371 閱讀 5667

內容:

1、什麼是morris遍歷

2、morris遍歷規則與過程

3、先序及中序

4、後序

5、morris遍歷時間複雜度分析

1、什麼是morris遍歷

關於二叉樹先序、中序、後序遍歷的遞迴和非遞迴版本,在這裡有詳細**:

明顯這6種遍歷演算法的時間複雜度都需要 o(h) (h 為樹高)的額外空間複雜度

另外因為二叉樹遍歷過程中只能向下查詢孩子節點而無法回溯父結點,因此這些演算法借助棧來儲存要回溯的父節點

並且棧要保證至少能容納下 h 個元素(比如遍歷到葉子結點時回溯父節點,要保證其所有父節點在棧中)

而morris遍歷則能做到時間複雜度仍為 o(n) 的情況下額外空間複雜度只需 o(1) 。 

2、morris遍歷規則與過程

首先在介紹morris遍歷之前,我們先把先序、中序、後序定義的規則拋之腦後,

比如先序遍歷在拿到一棵樹之後先 遍歷頭結點然後是左子樹後是右子樹,並且在遍歷過程中對於子樹的遍歷仍是這樣。

在忘掉這些遍歷規則之後,我們來看一下morris遍歷定義的標準:

(1)定義乙個遍歷指標 cur ,該指標首先指向頭結點

(2)判斷 cur 的左子樹是否存在

如果 cur 的左孩子為空,說明 cur 的左子樹不存在,那麼 cur右移(cur=cur.right)

如果 cur 的左孩子為cur,說明 cur 的左子樹存在,找出該左子樹上最右節點記為 mostright

如果mostright 的右孩子為空,那就讓其指向 cur ( mostright.right=cur ),並左移 cur ( cur=cur.left )

如果mostright 的右孩子不為空,那麼讓 cur 右移( cur=cur.right ),並將 mostright 的右孩子置空

(3)經過步驟2之後,如果 cur 不為空,那麼繼續對 cur 進行步驟2,否則遍歷結束

下圖所示舉例演示morris遍歷的整個過程:

3、先序及中序遍歷完成後對 cur 進過的節點序列稍作處理就很容易得到該二叉樹的先序、中序序列:

morris遍歷會來到乙個左孩子不為空的結點兩次,而其它結點只會經過一次

因此使用 morris遍歷列印先序序列時:

而使用morris遍歷列印中序序列時:

遍歷**如下:

1

public

static

void

morrispre(node head)

6 node cur =head;

7while (cur != null

) else

16if (mostright.right == null

) else24}

25}26system.out.println();27}

2829

public

static

void

morrisin(node head)

34 node cur =head;

35while (cur != null

) else

44if (mostright.right == null

) else52}

53}54system.out.println();

55 }

4、後序使用morris遍歷得到二叉樹的後序序列就沒那麼容易了,因為對於樹種的非葉結點,

morris遍歷都會經過它兩 次,而我們後序遍歷實在是在第三次來到該結點時列印該結點的。

因此要想得到後序序列,僅僅改變在morris遍歷時列印結點的時機是無法做到的。

morris實現後序遍歷:如果在每次遇到第二次經過的結點時,將該結點的左子樹的右邊界上的結點

從下到上列印,最後再將整顆樹的右邊界從下到上列印,終就是這個數的後序序列:

其中無非就是在morris遍歷中在第二次經過的結點的時機執行一下列印操作。

而從下到上列印一棵樹的右邊界,可以將該右邊界上的結點看做以 right 指標為後繼指標的鍊錶,

然後將其反轉reverse,然後列印,最後恢復成原始結構即可

**如下:

1

public

static

void

morrispos(node head)

6 node cur =head;

7while (cur != null

) else

15if (mostright.right == null

) else24}

25}26//

在這列印整顆樹的右邊界

27printrightedge(head);

28system.out.println();29}

3031

//列印節點下左子樹的右邊界

32private

static

void

printrightedge(node root)

36//

reverse the right edge

37 node cur =root;

38 node pre = null;39

while (cur != null

) 45

//print

46 cur =pre;

47while (cur != null

) 51

//recover

52 cur =pre;

53 pre = null;54

while(cur!=null

)60 }

5、morris遍歷時間複雜度分析因為morris遍歷中,只有左孩子非空的結點才會經過兩次而其它結點只會經過一次,也就是說遍歷的次數小於 2n

因此使用morris遍歷得到先序、中序序列的時間複雜度自然也是 o(n) ;

但產生後序序列的時間複雜度還要 算上 printrightedge 的時間複雜度,但是你會發現整個遍歷的過程中,所有的

printrightedge 加起來也只是 遍歷並列印了 n 個結點,因此時間複雜度仍然為 o(n) 

總結:

morris遍歷結點的順序不是先序、中序、後序,而是按照自己的一套標準來決定接下來要遍歷哪個結點

morris遍歷的獨特之處就是充分利用了葉子結點的無效引用(引用指向的是空,但該引用變數仍然佔記憶體),

從而實現了o(n)的時間複雜度和o(1)的空間複雜度

Morris 中序遍歷

morris 中序遍歷是另一種遍歷二叉樹的方法,它能將非遞迴的中序遍歷空間複雜度降為 o 1 如果 x 無左孩子,先將 xx 的值加入答案陣列,再訪問 xx 的右孩子,即 x x.textitx x.right。如果 xx 有左孩子,則找到 xx 左子樹上最右的節點 即左子樹中序遍歷的最後乙個節點,...

Morris 遍歷學習筆記

morris遍歷是實現了時間複雜度o n 空間複雜度只有o 1 的二叉樹遍歷的一種方法,它實現的方法主要為每個節點只遍歷一次,具體規則如下 個人感覺morris的本質就是將原先的單向遍歷加入了回溯,即有左子樹的節點在遍歷完左子樹後還可以再返回該節點,並向右繼續遍歷,也即實現了有左子樹的節點只遍歷2次...

二叉樹遍歷演算法 Morris演算法

morris演算法的時間複雜度是o n 空間複雜度為o 1 基本思想是 1.找到當前節點的前驅節點,即左節點的最右節點,若為空則指向當前節點,若為當前節點則表明該節點左邊以及該節點已經遍歷完,並將前驅節點的有指標恢復為空 2.若當前節點的左節點為空,則直接訪問當前節點右節點 前序遍歷和中序遍歷 差不...