牛客网在线编程专题《剑指offer-面试题25》二叉树中和为某一值的路径

我的个人微信公众号:Microstrong

微信公众号ID:MicrostrongAI

公众号介绍:Microstrong(小强)同学主要研究机器学习、深度学习、计算机视觉、智能对话系统相关内容,分享在学习过程中的读书笔记!期待您的关注,欢迎一起学习交流进步!

知乎专栏:https://zhuanlan.zhihu.com/Microstrong

Github:https://github.com/Microstrong0305

个人博客:https://blog.csdn.net/program_developer

题目链接:

https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=2&rp=2&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

题目描述:

解题思路:

        例如, 输入图1中的二叉树和整数22,则打印出两条路径,第一条路径包含节点10、12,第二条路径包含节点10、5和7。

        一般的数据结构和算法的教材都没有介绍树的路径, 因此对大多数应聘者而言,这是一个新概念,也就很难一下子想出完整的解题思路。 这时候我们可以试着从一两个具体的例子入手,找到规律。

        以图1中的二叉树作为例子来分析。 由于路径是从根节点出发到叶节点,也就是说路径总是以根节点为起始点,因此我们首先需要遍历根节点。在树的前序、中序、后序3种遍历方式中,只有前序遍历是首先访问根节点的。
        按照前序遍历的顺序遍历图1中的二叉树,在访问节点10之后,就会访问节点5。从二叉树节点的定义可以看出,在本题的二叉树节点中没有指向父节点的指针,当访问节点5的时候,我们是不知道前面经过了哪些节点的, 除非我们把经过的路径上的节点保存下来。每访问一个结点,我们都把当前节点添加到路径中去。当到达节点5时, 路径中包含两个节点,它们的值分别是10和5。接下来遍历到节点4,我们把这个节点也添加到路径中。这时候已经到达叶节点,但路径上3个节点的值之和是19。这个 和不等于输入的值22,因此不是符合要求的路径。

图1:二叉树中有两条和为22的路径:一条路径经过结点10、5、7;另一条路径经过结点10、12。

         我们接着要遍历其他的节点。在遍历下一个节点之前,先要从节点4回到节点5,再去遍历节点5的右子节点7。值得注意的是,当回到节点5的时候,由于节点4已经不在前往节点7的路径上了,所以我们需要把节点4从路径中删除。接下来访问节点7的时候,再把该节点添加到路径中。此时路径中的3个节点10、5、7之和刚好是22,是一条符合要求的路径。
         我们最后要遍历的是节点12。在遍历这个节点之前,需要先经过节点5回到节点10。同样,每次当从子节点回到父节点的时候,我们都需要在路径上删除子节点。最后在从节点10到达节点12的时候,路径上的两个节点的值之和也是22,因此,这也是一条符合要求的路径。

           我们可以用表1总结上述分析过程。

表1:遍历图1中的二叉树的过程
步骤 操作 是否叶节点 路径 路径节点值的和
1 访问节点10 节点10 10
2 访问节点5 节点10、节点5 15
3 访问节点4 节点10、节点5、节点4 19
4 访问节点5   节点10、节点5 15
5 访问节点7 节点10、节点5、节点7 22
6 访问节点5   节点10、节点5 15
7 访问节点10   节点10 10
8 访问节点12 节点10、节点12 22

         分析完前面具体的例子之后,我们就找到了一些规律。当用前序遍历的方式访问到某一节点时,我们把该节点添加到路径上, 并累加该节点的值。如果该节点为叶节点,并且路径中节点值的和刚好等于输入的整数,则当前路径符合要求,我们把它打印出来。如果当前节点不是叶节点,则继续访问它的子节点。当前节点访问结束后,递归函数将自动回到它的父节点。因此,我们在函数退出之前要在路径上删除当前节点并减去当前节点的值,以确保返回父节点时路径刚好是从根节点到父节点。我们不难看出保存路径的数据结构实际上是一个栈,因为路径要与递归调用状态一致,而递归调用的本质就是一个压栈和出栈的过程。

    形成了清晰的思路之后,就可以动手写代码了。

已经AC的代码:

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:

     # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        if not root:
            return []
        res = []
        path = []
        self.subPath(root, res, path, expectNumber, 0)
        return res

    def subPath(self, root, res, path, expectNumber, currentNum):
        currentNum += root.val
        # root使用append转换成了列表,因为最后要一个序列,root本身还是树节点结构
        path.append(root)
        # 判断是不是到叶子节点了,到叶子节点了就要判断值的和是不是相等
        if not root.left and not root.right:
            if currentNum == expectNumber:
                onePath = []
                for node in path:
                    onePath.append(node.val)
                res.append(onePath)
        if currentNum < expectNumber:
            if root.left:
                self.subPath(root.left, res, path, expectNumber, currentNum)
            if root.right:
                self.subPath(root.right,res, path, expectNumber, currentNum)

        # 拿到一个正确的路径后要弹出,回到上一次父节点继续递归
        path.pop()

if __name__ == "__main__":
    sol = Solution()
    n1 = TreeNode(10)
    n2 = TreeNode(5)
    n3 = TreeNode(12)
    n4 = TreeNode(4)
    n5 = TreeNode(7)
    n1.left = n2
    n1.right = n3
    n2.left = n4
    n2.right = n5
    print(sol.FindPath(n1, 22))

Reference:

【1】《剑指offer》,何海涛著。

【2】https://drivingc.com/p/5c75fd134b0f2b0a081321e3#34.%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%92%8C%E4%B8%BA%E6%9F%90%E4%B8%80%E5%80%BC%E7%9A%84%E8%B7%AF%E5%BE%84

【3】https://github.com/leeguandong/Interview-code-practice-python/blob/master/%E5%89%91%E6%8C%87offer/%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%92%8C%E4%B8%BA%E6%9F%90%E4%B8%80%E5%80%BC%E7%9A%84%E8%B7%AF%E5%BE%84.py

发布了285 篇原创文章 · 获赞 892 · 访问量 111万+

猜你喜欢

转载自blog.csdn.net/program_developer/article/details/100149617
今日推荐