Leetcode501 Morris中序遍歷

2021-10-09 18:01:25 字數 3892 閱讀 9128

一般二叉樹的中序遍歷需要用到遞迴演算法,在遞迴過程中,程式會自動使用遞迴棧,保證了我們可以在遍歷完左子樹後,返回到當前節點繼續後面的遍歷。但是遞迴棧也占用了部分空間,因此出現morris中序遍歷方法,使得我們中序遍歷的空間複雜度變為o(1),即與樹的深度無關。

我們結合leetcode中第501題二叉搜尋樹中的眾數,來學習morris中序遍歷演算法

以下使用python3~

本部分參考leetcode501官方題解

在中序遍歷的時候,一定先遍歷左子樹,然後遍歷當前節點,最後遍歷右子樹。在常規方法中,我們用遞迴回溯或者是棧來保證遍歷完左子樹可以再回到當前節點,但這需要我們付出額外的空間代價。

我們需要用一種巧妙地方法可以在 o(1) 的空間下,遍歷完左子樹可以再回到當前節點。我們希望當前的節點在遍歷完當前點的前驅之後被遍歷,我們可以考慮修改它的前驅節點的 right 指標。

這樣做的原因在於,我們希望通過前驅節點能夠找到當前應該遍歷的節點。通過使前驅節點的 right 指標指向當前節點,就可以實現這個想法。
在morris中序遍歷的程式執行中,實際抽象過程如下:

初始化乙個cur指標,從根節點一直走到其左子樹為空的節點。在這「一路向左」的過程中,不對節點值進行遍歷,只是經過。同時要做一件重要的事情:使cur節點的前驅節點的 right 指標指向它。

當cur指標走到了樹的最左葉子節點後,遍歷該節點值。之後,它要沿著上一步設定好的right指標,往回走(一路向右),這次在走的過程中,需要對路過的節點值進行遍歷。

為了實現上面的抽象過程,程式中的具體演算法如下:

如果當前節點沒有左子樹,則遍歷這個點,然後跳轉到當前節點的右子樹。

如果當前節點有左子樹,那麼它的前驅節點一定在左子樹上,我們可以在左子樹上一直向右行走,找到當前點的前驅節點。

如果前驅節點沒有右子樹,就將前驅節點的 right 指標指向當前節點。這一步是為了在遍歷完前驅節點後能找到前驅節點的後繼,也就是當前節點。

如果前驅節點的右子樹為當前節點,說明前驅節點已經被遍歷過並被修改了 right 指標,這個時候遍歷當前的點,然後跳轉到當前節點的右子樹,繼續後面的遍歷。

**實現:

def

morris_inorder

(root)

: cur, pre = root,

none

# pre為前驅節點, cur為當前節點

while cur:

ifnot cur.left:

# cur走到最左..

....

.# 遍歷cur.val

cur = cur.right

continue

pre = cur.left

while pre.right and pre.right != cur:

pre = pre.right

ifnot pre.right:

pre.right = cur

cur = cur.left

else:.

....

..# 遍歷cur.val

這道題可以用最簡單的思路求解,比如,使用普通的中序遍歷演算法獲得乙個樹的雜湊表或字典,記錄每個節點值出現的次數,取出現次數最多的那乙個(或幾個)值作為結果返回。但是這樣做空間複雜度較高,我們現在希望用o(1)的空間複雜度解決這個問題。那麼我們需要改變兩個方面:

1. 將普通的中序遍歷替換為morris中序遍歷。

2. 將「使用雜湊表/字典找出現次數最多的值」替換為其他方法。

第乙個方面我們已經知道如何做了,下面就是對第二點的改進。

其實尋找出現最多的數,在我們遍歷二叉搜尋樹的時候就可以順便做到。

在中序遍歷有重複數字的二叉搜尋樹時,我們獲得的數字一定是按照單調非減順序排列的

我們可以設定幾個值來記錄上一次獲得的值base、當前獲得的值cur_val、當前值已經出現的次數count,和當前元素出現次數的最大值max。還要維護乙個存放當前已經找到的眾數的列表ans。

當cur_val=base時,count += 1;如不相等,更新base為cur_val,count=1(重新計數)

當count=max時,說明當前值也是乙個眾數,將cur_val放入ans中;

若count>max,說明當前值出現次數比之前找到的眾數要多,則更新ans=[cur_val],max=count。

上述的過程我們封裝到update函式中,在每遍歷到乙個節點時,執行該函式即可。

# definition for a binary tree node.

# class treenode:

# def __init__(self, x):

# self.val = x

# self.left = none

# self.right = none

class

solution

:def

findmode

(self, root: treenode)

-> list[

int]

: self.ans, self.count, self.max, self.base =

,0,0

,0# 相當於全域性變數

defupdate

(val)

:# 更新眾數列表

ifnot self.ans:

# 用遍歷獲得的第乙個數字初始化所有相關變數

self.count, self.base, self.max =

1, val,

1return

if val==self.base: self.count +=

1else

: self.count =

1 self.base = val

elif self.count>self.max:

self.ans =

[val]

self.max = self.count

defmorris_inorder

(root)

:# morris中序遍歷

cur, pre = root,

none

while cur:

ifnot cur.left:

update(cur.val)

# 使用當前節點值更新眾數列表

cur = cur.right

continue

pre = cur.left

while pre.right and pre.right != cur:

pre = pre.right

ifnot pre.right:

pre.right = cur

cur = cur.left

else

: update(cur.val)

# 使用當前節點值更新眾數列表

cur = cur.right

morris_inorder(root)

return self.ans

leetcode 兩數相加(騰訊50 1

思路 leetcode中已經設定了鍊錶,只需遍歷兩個待相加的列表,逐次相加即可。思想 分為三種情況,兩個相加的數長度相等,逐個遍歷進行相加。需處理滿10進1,設定乙個flag列表處理對應位置相加後 10的結果。便於遍歷下乙個位置時進行相加。即處理進1的操作 兩個數不等。提前乙個結束 在上述現象中,即...

leetcode騰訊精選練習50(1) 兩數相加

這是一道來自leetcode的演算法題目。給出兩個非空的鍊錶用來表示兩個非負的整數。其中,它們各自的位數是按照逆序的方式儲存的,並且它們的每個節點只能儲存 一位 數字。如果,我們將這兩個數相加起來,則會返回乙個新的鍊錶來表示它們的和。您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。示例 輸...

leetcode 501 二叉搜尋樹中的眾數

給定乙個有相同值的二叉搜尋樹 bst 找出 bst 中的所有眾數 出現頻率最高的元素 假定 bst 有如下定義 結點左子樹中所含結點的值小於等於當前結點的值 結點右子樹中所含結點的值大於等於當前結點的值 左子樹和右子樹都是二叉搜尋樹 例如 給定 bst 1,null,2,2 返回 2 高階 你可以不...