1. 程式人生 > >二叉樹的中序遍歷,每層遍歷,以及Z字形遍歷

二叉樹的中序遍歷,每層遍歷,以及Z字形遍歷

leetcode中,二叉樹也是一塊重點,對於樹結構衍生出的問題,一般用遞迴的方法會比較多。
在這裡插入圖片描述
如上圖的二叉樹:
中序遍歷:左-根-右的順序,結果是[4,2,5,1,6,3]
每層遍歷:從最上面那層往下面讀,結果是[1,2,3,4,5,6]
Z字形遍歷:和每層遍歷差不多,只是走Z字,結果是[1,3,2,4,5,6]

二叉樹的中序遍歷

對於二叉樹的中序遍歷,就是左-根-右這種遍歷順序。
有遞迴和迭代寫法:非遞迴就用棧來實現

        res, stack = [], []
        while True:
            while root:
                stack.append(root)
                root = root.left
            if not stack:
                return res
            node = stack.pop()
            res.append(node.val)
            root = node.right

我們看棧的情況,while true保證了直到棧為空(if not stack)的時候我們結束迴圈,這時候我們已遍歷棧中所有所有元素。
while root讓我們一直會保證左邊的元素會先進棧,直到左邊沒有元素為止,.pop()彈出後入棧的,後入棧的是最底層的左側元素。
比如第一次while true迴圈,1,2,4先進stack棧,4的左邊沒有元素,所以把4先出棧到res裡,再將root更新成4的右孩子,現在stack: [1,2], res: [4]
第二次while true迴圈,4的右孩子不存在,會執行2出棧,將root更新成2的右孩子,現在stack: [1], res: [4,2]
第三次 while true迴圈, 5進stack棧,5的左邊沒有元素,5出棧到res裡,將root更新成5的右孩子,stack: [1], res:[4,2,5]

這個的思想就是,while root和root=root.left保證了左子樹的元素紛紛統統入棧,當我們要考慮一個元素的右孩子時,就說明這個元素已經被我們出棧了。一個元素的右孩子也為空就說明我們已經走到了最左側,應該往上一層考慮了。這裡的root = node.right很巧妙(下一次迴圈與這一次計算結果有關,迭代)。

遞迴寫法:

def inorderTraversal1(self, root):
    res = []
    self.helper(root, res)
    return res
    
def helper(self, root, res):
    if root:
        self.helper(root.left, res)
        res.append(root.val)
        self.helper(root.right, res)

這個還是挺直觀的,按左-根右的順序呼叫函式helper(),函式呼叫棧會是helper(1.left), append(1), helper(1.right),再對helper(1.left)的函式呼叫棧進行分析,總是左-根-右的順序。

每層遍歷

要求輸出的是一個二維列表,每個元素是每層元素所組成的列表。
比如示例就應該輸出[[1],[2,3],[4,5,6]]

def levelOrder(self, root):
    if not root:
        return []
    ans, level = [], [root]
    while level:
        ans.append([node.val for node in level])
        temp = []
        for node in level:
            temp.extend([node.left, node.right])
        level = [leaf for leaf in temp if leaf]  #只把非None的孩子新增進去
    return ans

思想就是開枝散葉的思想,有兩個列表很關鍵,一個是結果列表res存我們的結果,一個是每層元素列表level存每層的樹結點元素。只要這層不是空的,我就把這層元素新增進ans裡,再用extend方法將孩子列表做好(對每個本層元素都開枝散葉它下層元素),並更新層元素列表。

Z字形遍歷

要求輸出的是一個二維列表,每個元素是每層元素所組成的列表。
比如示例就應該輸出[[1],[3,2],[4,5,6]]

思想和上述一樣,就是加個flag對第偶數次遍歷反序

    def zigzagLevelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
        ans, level = [], [root]
        flag = 1
        while level:
            ans.append([node.val for node in level][::flag]) #偶數次反序
            temp = []
            for node in level:
                temp.extend([node.left, node.right])
            level = [leaf for leaf in temp if leaf]  #只把非None的孩子新增進去
            flag *= -1
        return ans

總結一下:
1.對於樹的中序遍歷,遞迴好寫很多,迭代不太直觀
2.對於樹的每層遍歷,extend一次加多個元素,level = [leaf for leaf in temp if leaf] 這個寫的不錯
3.在Z字形遍歷中,flag的妙用,我一開始想的話就會用counter,迴圈一次加1,如果偶數次就逆向輸出,程式碼太不簡潔了。