LeetCode刷題實戰94:二叉樹的中序遍歷

算法的重要性,我就很少說了吧,想去大廠,就必需要通過基礎知識和業務邏輯面試+算法面試。因此,爲了提升你們的算法能力,這個公衆號後續天天帶你們作一道算法題,題目就從LeetCode上面選 !html

今天和你們聊的問題叫作 二叉樹的中序遍歷,咱們先來看題面:node

https://leetcode-cn.com/problems/binary-tree-inorder-traversal/面試

Given the root of a binary tree, return the inorder traversal of its nodes' values.
算法

題意


給定一個二叉樹的根節點 root ,返回它的 中序 遍歷。用遞歸作這道題很是簡單,你能不用遞歸實現嗎?

樣例

解題

https://www.cnblogs.com/techflow/p/13587858.html
咱們先來介紹一下二叉樹的中序遍歷,二叉樹有三種遍歷方式,分別是 先序、中序和後序。對於初學者而言,可能會以爲這三種順序傻傻分不清楚,不知道它們之間有什麼分別。其實說白了很是簡單,遍歷方式其實指的是 咱們在遞歸遍歷的時候的選擇順序
假設咱們目前遞歸到的節點是u,它有左右兩個孩子。在保證左孩子必定先於右孩子訪問的前提下,咱們有三種策略。第一種是先把u加入訪問序列,以後再分別遍歷左右子樹,第二種是先遞歸遍歷左子樹,而後把u加入訪問序列,最後再遍歷右子樹。第三種策略是先遞歸遍歷左右子樹,最後再把u加入訪問序列。
這三種策略之間有什麼不一樣呢?其實最大的不一樣就在於 u加入訪問序列的順序不一樣,若是是先加入u再訪問,那麼就是先序。若是是先訪問了左子樹再來加入u,則是中序,最後若是是先遞歸了左右子樹,最後再加入u,則是後序。說白了也就是u先加入就是先序,中間加入就是中序,最後加入就是後序。若是你還以爲有點蒙的話,咱們來看下代碼就很是清晰了。

# 先序
def dfs(u):
    visited.append(u)
    dfs(u.left)
    dfs(u.right)
    
# 中序
def dfs(u):
    dfs(u.left)
    visited.append(u)
    dfs(u.right)
    
    
# 後序
def dfs(u):
    dfs(u.left)
    dfs(u.right)
    visited.append(u)數組


可是題目當中要求咱們不經過遞歸來實現,這該怎麼辦呢?
其實也有辦法,咱們須要 從遞歸的實現原理入手。咱們知道在編譯器內部,當咱們從一個函數調用另一個函數的時候,這些函數的信息會被存儲在一個棧結構內。棧中間的每個節點會記錄函數的名稱以及它目前運行的位置,以及一些中間變量等等。因此當咱們遞歸的時候,其實就是當前的函數不停的入棧的過程。
好比咱們dfs函數在第5行代碼處遞歸調用了dfs函數,那麼編譯器內部的堆棧會記錄[(dfs, 5), (dfs, 1)]。若是咱們在dfs的第一行又調用了sum函數,那麼編譯器又會把sum這個函數加入堆棧,變成:[(dfs, 5), (dfs, 1), (sum, 1)]。當函數執行完成以後,會從棧中彈出。
簡而言之, 遞歸其實就是利用編譯器自行維護的棧結構來簡化咱們代碼和功能的編寫。既然這道題當中要求咱們不能使用遞歸,那麼咱們就只能本身來使用棧來模擬這個過程了。因爲咱們本身須要維護棧當中的元素,使得整個過程會稍微複雜一些。
在這道題目當中,咱們使用棧來記錄樹上的節點。棧頂的節點便是當前訪問的節點。

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        stack = []
        stack.append(root)
        while len(stack) > 0:
            # 獲取棧頂頂點
            cur = stack[-1]
            if cur is None:
                stack.pop()
                continue
                
            # 若是左孩子存在,那麼優先遍歷左孩子
            if cur.left is not None:
                stack.append(cur.left)
                # 把左指針置爲空,防止死循環
                # 這裏也能夠用set來維護
                cur.left = None
                continue
                
            # 左邊遍歷結束以後加入序列
            ret.append(cur.val)
            stack.pop()
            if cur.right is not None:
                stack.append(cur.right)        
        return ret微信


若是隻是二叉樹的遍歷,那這個誰都會,但若是不能使用遞歸,那麼就很考驗硬實力了。須要咱們對遞歸的底層原理有深刻的理解,而且熟悉棧這個數據結構的使用。這段代碼的邏輯不難理解,但實現仍是挺鍛鍊人的,推薦你們試試。
好了,今天的文章就到這裏,若是以爲有所收穫,請順手點個在看或者轉發吧,大家的支持是我最大的動力。


上期推文:數據結構

LeetCode50-80題彙總,速度收藏!
LeetCode刷題實戰81:搜索旋轉排序數組 II
LeetCode刷題實戰82:刪除排序鏈表中的重複元素 II
LeetCode刷題實戰83:刪除排序鏈表中的重複元素
LeetCode刷題實戰84: 柱狀圖中最大的矩形
LeetCode刷題實戰85:最大矩形
LeetCode刷題實戰86:分隔鏈表
LeetCode刷題實戰87:擾亂字符串
LeetCode刷題實戰88:合併兩個有序數組
LeetCode刷題實戰89:格雷編碼
LeetCode刷題實戰90:子集 II
LeetCode刷題實戰91:解碼方法
LeetCode刷題實戰92:反轉鏈表 II
LeetCode刷題實戰93:復原IP地址

本文分享自微信公衆號 - 程序IT圈(DeveloperIT)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。app

相關文章
相關標籤/搜索