二叉树算法合集【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
8. 搜索二叉树路径

下面三种不同的题型都用的是相同的模板,仅供参考
8.1 搜索所有路径:递归函数作用 是从当前节点node往下搜索路径,当前node一定不为空

  1. 首先将node加入到路径中
  2. 如果当前节点是叶子节点,路径到尽头,完成一条路径的搜索
  3. 如果当前节点不是叶子节点,有左子节点,就递归搜索左子树。右子节点也一样
    def binaryTreePaths(self, root):
        """
        找所有到叶子节点的路径
        :param root:
        :return: list[str]
        """
        if root is None:
            return []

        res = []

        def _backtrace(node, pre_list):
            new_list = pre_list
            new_list += str(node.val)
            if not node.left and not node.right:  # 1. 到了叶子节点
                res.append(new_list)
                return
            # 2. 不是叶子节点
            if node.left:
                _backtrace(node.left, new_list + "->")
            if node.right:
                _backtrace(node.right, new_list + "->")

        _backtrace(root, "")
        return res

8.2 求是否存在路径和等于指定值的路径

    def hasPathSum(self, root, sum):
        if root is None:
            return False

        res = False
        def _backtrace(node, left_sum):
            nonlocal res
            if res:
                return
            # 直接开始判断,是叶子节点,且值为left_sum
            if not node.left and not node.right and node.val == left_sum:
                res = True
                return
            else:
                if node.left:
                    _backtrace(node.left, left_sum-node.val)
                if node.right:
                    _backtrace(node.right, left_sum-node.val)

        _backtrace(root, sum)
        return res

8.3 求路径和等于指定值的路径:递归函数的作用是从当前节点node往下搜索路径和等于left_sum的路径,当前node一定不为空。

  1. 首先将当前节点node加入到路径中
  2. 如果当前节点是叶子节点,路径到尽头,根据判断条件加入到res中
  3. 如果当前节点不是叶子节点,有左子节点,就递归搜索左子树。右子节点也一样
    def pathSum(self, root, sum):
        if not root:
            return []

        res = []
        def _backtrace(node, left_sum, pre_list):
            new_list = pre_list.copy()  # 注意要拷贝一份,不然是引用变量,后面递归体会改变pre_list
            new_list.append(node.val)  # 进这个函数的都是node不为空
            # 1.叶子节点
            if not node.left and not node.right and node.val == left_sum:
                res.append(new_list)
            else:#  2. 非叶子节点
                if node.left:  # 左子树不为空
                    _backtrace(node.left, left_sum-node.val, new_list)
                if node.right:
                    _backtrace(node.right, left_sum-node.val, new_list)

        _backtrace(root, sum, [])
        return res

8.4 计算从根到叶子节点生成的所有数字之和

    def sumNumbers(self, root):
        """
        所有路径序列的值 加起来之和
        """
        if root is None:
            return 0

        res = []
        sum = 0

        def _backtrace(node, pre_list):
            new_list = pre_list  # 字符串的拷贝直接相等即可,不需要调用copy函数
            new_list += str(node.val)
            if not node.left and not node.right:
                res.append(new_list)
            else:
                if node.left:
                    _backtrace(node.left, new_list)
                if node.right:
                    _backtrace(node.right, new_list)

        _backtrace(root, "")
        for i in res:
            sum += int(i)
        return sum
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值