二叉樹算法合集【python】【遞歸+非遞歸】

下面是整理的一些二叉樹的常見問題,包括求最大最小深度、前中後序以及層序遍歷的遞歸和非遞歸解法、由前中序序列構建樹等。

1. 求最大深度

1.1 遞歸解法:非常清晰,遞歸邏輯即分別求左右子樹高度,取大值加一即可

 def maxDepth(self, root):
        if root is None:
            return 0
        lefth = self.maxDepth(root.left)
        righth = self.maxDepth(root.right)
        return max(lefth, righth) + 1

1.2 非遞歸解法:其實就是層序遍歷,用BFS和隊列的搭配使用可破。

    def maxDepthBFS(self, root):
        """
        bfs求最大深度,bfs與隊列黃金搭配
        :param root:
        :return:
        """
        if root is None:
            return 0

        q = [root]
        depth = 0
        while q:
            depth += 1
            iter = len(q)  # 當前隊列中的個數即爲該層的節點個數,需要記錄下來
            for i in range(iter): # 然後一個一個pop出去,同時把子節點壓進去
                cur_node = q.pop(0)
                if cur_node.left is not None:
                    q.append(cur_node.left)
                if cur_node.right is not None:
                    q.append(cur_node.right)
        return depth

2. 求最小深度

遞歸解法跟最大深度類似,但要注意子樹爲空時的問題:在求最大深度時,子樹爲空不妨礙最大深度的值,但是最小深度時,若左子樹爲空,那麼最小深度只能來自於右子樹深度,而不是0。

下面代碼窮舉左右子樹是否爲空的四種情況,比較清晰

    def minDepth(self, root):
        if root is None:
            return 0
        # 兩個子樹都不空
        if root.left is not None and root.right is not None:
            return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
        # 有一個子樹爲空,則取大值
        elif root.left is None and root.right is None:
            return self.minDepth(root.right) + 1
        elif root.right is None and root.left is None:
            return self.minDepth(root.left) + 1
        # 兩個子樹都爲空
        else:
            return 1


3. 層序遍歷

3.1 遞歸解法:遞歸函數需要有一個參數level,該參數表示當前結點的層數。遍歷的結果返回到一個二維列表res=[[]]中,res中的每一個子列表保存了對應index層的從左到右的所有結點value值。

    def levelOrder_recur(self, root):
        res =[[]]

        def helper(node, level):
            # 遞歸函數需要有一個參數level,該參數表示當前結點的層數。
            if not node:
                return
            else:
                res[level-1].append(node.val)  # 加入到對應層中
                if len(res) == level:
                    res.append([])  # 再擴展一行
                helper(node.left, level+1)
                helper(node.right, level+1)

        helper(root, 1)
        return res[:-1]  # 不算最後一行,因爲前一層擴展了一個空行

3.2 非遞歸解法:求最大深度時已經涉及到,隊列中保存同一層的所有節點,通過同時壓出和壓入來體現同層的關係

 def levelOrder(self, root):
        """
        :param root:
        :return: 層序遍歷,並返回層序的列表
        """
        if root is None:
            return
        queue = []
        queue.append(root)
        result = []
        depth = -1
        while queue:
            depth += 1
            result.append([])  # 每多一層就擴展一行

            # 記錄下當前隊列中節點的個數,即爲該層的節點個數
            iter = len(queue)
            for x in range(iter):
                # 這個循環是指把同一層的節點全部pop出去,並且同時把下一層的節點push進來
                node = queue.pop(0)
                result[depth].append(node.val)
                if node.left is not None:
                    queue.append(node.left)
                if node.right is not None:
                    queue.append(node.right)
        return result

擴展
Q:如果仍然按層遍歷,但是每層從右往左遍歷怎麼辦呢?

A:將上面的代碼left和right互換即可

Q:如果仍然按層遍歷,但是我要第一層從左往右,第二層從右往左,第三從左往右…這種zigzag遍歷方式如何實現?

A:將res[level-1].append(node.val)進行一個層數奇偶的判斷,一個用append(),一個用insert(0,)

4. 前序遍歷

4.1 遞歸解法:中左右的順序,保存到列表中,注意兩個列表的相連是+

    def preorder_recur(self, root):
        if root is None:
            return []
        return [root.val] + self.preorder(root.left) + self.preorder(root.right)

4.2 非遞歸解法:用res保存結果,用cur代表當前節點,用stack來存放當前節點的右子節點,然後按照一直往左延伸下去

    def preorder(self, root):
        stack = []
        res = []
        cur = root
        while stack or cur:
            if cur:
                res.append(cur.val)
                stack.append(cur.right)
                cur = cur.left
            else:
                cur = stack.pop()  # 取出保存的右子節點

        return res


5. 中序遍歷

5.1 遞歸解法

    def inorder_recur(self, root):
        if root is None:
            return []
        return self.inorder_recur(root.left) + [root.val] + self.inorder_recur(root.right)

5.2 非遞歸:保存當前節點到棧中,從棧取出同時加入結果,並移到右子樹

    def inorder(self, root):
        stack = []
        res = []
        cur = root
        while stack or cur:
            if cur:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res


6. 後序遍歷

6.1 遞歸

    def postorder_recur(self, root):
        if not root:
            return []
        return self.postorder_recur(root.left) + self.postorder_recur(root.right) + [root.val]

6.2 非遞歸:左右中的順序,倒過來是中右左的順序,跟前序遍歷只是左右相反,因此可以把前序遍歷的左右交換,最後逆序輸出列表,即爲答案

    def postorder(self,root):
        """
        左右中 是中右左的逆序 只要把preorder左右互換即可
        :param root:
        :return:
        """
        stack = []
        res = []
        cur = root
        while stack or cur:
            if cur:
                res.append(cur.val)
                stack.append(cur.left)
                cur = cur.right
            else:
                cur = stack.pop()
        return res[::-1]


7. 根據前序、中序序列構建樹/求出後序序列

通過前序序列獲取根節點,在中序序列中根據根節點劃分爲左右子樹

    def getTreePreMid(self, pre, mid):
        """
        前序 中序 構建樹
        :param pre:
        :param mid:
        :return: root
        """
        if len(pre) == 0:
            return None
        if len(pre) == 1:
            return TreeNode(pre[0])
        root = pre[0]  # 前序序列第一個值就是根節點
        root_idx = mid.index(root)  # 找到該值在中序序列中的位置
        root.left = self.getTreePreMid(pre[1:1+root_idx], mid[0:root_idx])  # 左右兩部分分別遞歸求解
        root.right = self.getTreePreMid(pre[1+root_idx:], mid[root_idx+1:])
        return root
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章