6.2 数据结构---树(路径)

一、路径

1.二叉树的所有路径 leetcode 257

思路:深度遍历

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
 
class Solution:
    def __init__(self):
            self.paths = []
             
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        def dfs(root,path):  
            if not root.left and not root.right:#叶子节点
                path += str(root.val)
                self.paths.append(path)
                return
             
            path += str(root.val)+'->'
            if root.left:
                dfs(root.left,path)
            if root.right:
                dfs(root.right,path)
             
        if root == None:
            return []
        elif not root.left and not root.right:
            return [str(root.val)]
        else:
            dfs(root,'')
            return self.paths
         
        

  

2.求根到叶子节点数字之和 leetcode 129

 

 

 

 

思路:深度遍历,递归保存每一条路径

这里用int(s)就可以将以0开头的字符串'00123...'转成数字,或者将很多0组成的‘000...’转成0

‘001’--> int('001')=1

'000'-->int('000')=0

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
 
class Solution:
    def __init__(self):
            self.paths = []
             
    def sumNumbers(self, root: TreeNode) -> int:
        def dfs(root,path):
            if not root.left and not root.right:#叶子
                path += str(root.val)
                self.paths.append(path)
                return
            path += str(root.val)
            if root.left:
                dfs(root.left,path)
            if root.right:     
                dfs(root.right,path)
         
        if not root:
            return 0
        if not root.left and not root.right:
            return root.val
        path = ''
        dfs(root,path)
        res = 0
        # print(self.paths)
        for i_path in self.paths:
            if len(i_path) * '0' == i_path:#全0
                continue
            elif i_path.startswith('0'):#以0开头
                while len(i_path) > 0:
                    if i_path.startswith('0'):
                        i_path = i_path[1:]
                    else:
                        print(i_path)
                        res += eval(i_path)
                        break
            else:#正常
                print(eval(i_path))
                res += eval(i_path)

  

二、路径总和

1.路径总和  leetcode112

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

  

思路: 用栈将递归转成迭代的形式。深度优先搜索在除了最坏情况下都比广度优先搜索更快。 最坏情况是指满足目标和的 root->leaf 路径是最后被考虑的,这种情况下深度优先搜索和广度优先搜索代价是相通的。 利用深度优先策略访问每个节点,同时更新剩余的目标和。 所以我们从包含根节点的栈开始模拟,剩余目标和为 sum - root.val。 然后开始迭代:弹出当前元素,如果当前剩余目标和为 0 并且在叶子节点上返回 True; 如果剩余和不为零并且还处在非叶子节点上,将当前节点的所有孩子以及对应的剩余和压入栈中。

时间复杂度:和递归方法相同是O(N)。 空间复杂度:当树不平衡的最坏情况下是O(N) 。在最好情况(树是平衡的)下是 O(log N)。 

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

class Solution:
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        if not root:
            return False

        de = [(root, sum - root.val), ]
        while de:
            node, curr_sum = de.pop()
            if not node.left and not node.right and curr_sum == 0:
                return True
            if node.right:
                de.append((node.right, curr_sum - node.right.val))
            if node.left:
                de.append((node.left, curr_sum - node.left.val))
        return False

2.路径总和|  leetcode113

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

思路:

  1. 从根节点深度遍历二叉树,先序遍历时,将该节点值存储值path栈中,使用path_value累加节点值;
  2. 当遍历至叶节点时,检查path_value值是否为sum,若为sum,则将path push进入result中;
  3. 在后续遍历时,将该节点值从path栈中弹出,path_value减去节点值。

代码1:用栈

class Solution:
    def pathSum2(self, root, target, ):
        self.result = []
        path = []
        def helper(root, target, path):
            if root == None:
                return
            path.append(root.val)
            if (root.left == None) and (root.right == None) and (sum(path) == target):
                tmp = path[:]
                self.result.append(tmp)
            helper(root.left, target, path)
            helper(root.right, target, path)
            path.pop()

        helper(root, target, path)
        return self.result

代码2:

class Solution (object):
    def pathSum(self,root,sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: List[List[int]]
        """
        res = []
        if not root: return []

        def helper(root, sum, tmp):
            if not root:
                return
            if not root.left and not root.right and sum - root.val == 0:
                tmp += [root.val]
                res.append (tmp)
                return
            helper (root.left, sum - root.val, tmp + [root.val])
            helper (root.right, sum - root.val, tmp + [root.val])

        helper (root, sum, [])
        return res

 

3.路径总和||  leetcode437

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

示例:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

返回 3。和等于 8 的路径有:

1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11

三、最大路径和

1.最大路径和 leetcode124

题目:给定一棵二叉树,求各个路径的最大和,路径可以以任意节点作为起点和终点。

思路:后序遍历,每次递归的时候只返回左子树/右子树+root.data,最大值是全局记录的,所以不需要返回

最大路径和:根据当前节点的角色,路径和可分为两种情况:
一:以当前节点为根节点
1.只有当前节点
2.当前节点+左子树🌲
3.当前节点+右子树🌲
4.当前节点+左右子树 
这四种情况的最大值即为以当前节点为根的最大路径和
此最大值要和已经保存的最大值比较,得到整个树的最大路径值

二:当前节点作为父节点的一个子节点
和父节点连接的话则需取【单端的最大值】
1.只有当前节点
2.当前节点+左子树🌲
3.当前节点+右子树🌲
这三种情况的最大值

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

class IntRef:
    def __init__(self):
        self.val = None

class Solution:
    def findMaxPathRecursive(self,root,maxs):
        if root == None:
            return 0
        leftmax = self.findMaxPathRecursive(root.left,maxs)
        rightmax = self.findMaxPathRecursive(root.right,maxs)
        tmp = max(max(leftmax+root.data,rightmax+root.data),leftmax+rightmax+root.data)
        if tmp > maxs.val:
            maxs.val = tmp
            print(maxs)
        submax = leftmax if leftmax > rightmax else rightmax
        return root.data + submax

    def findMaxPath(self,root):
        maxs = IntRef()
        maxs.val = -2 **31
        self.findMaxPathRecursive(root,maxs)
        return maxs.val

if __name__ == '__main__':
    root = TreeNode(2)
    left = TreeNode(3)
    right = TreeNode(5)
    root.lef= left
    root.right = right
    S = Solution()
    maxs = S.findMaxPath(root)
    print(maxs)

  

四、最长同值路径 

1.最长同值路径 leetcode687

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。

示例1:
输入:
              5
             / \
            4   5
           / \   \
          1   1   5
输出:2

示例 2:
输入:
              1
             / \
            4   5
           / \   \
          4   4   5
输出:2
注意: 给定的二叉树不超过10000个结点.树的高度不超过1000。 

思路:先解释一下题目,就是让我们找到一个路径,这个路径上面的值都相同,而且可以不经过根节点,例如,例2中的4-4-4这样的。 可以使用递归来做,首先,求出以每个节点为根节点的最长路径,然后从底向上,判断与父亲节点的值是否相同,如果相同,就把当前结点最长的一个分支路径加上1返回给父节点。其中,可以把最长路径保存到一个全局变量中。

class Solution (object):
    def longestUnivaluePath(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        maxL = 0
        def getMaxL(node,val):
            nonlocal maxL #用来在函数或其他作用域中使用外层(非全局)变量。
            if node == None:
                return 0
            leftMaxL = getMaxL(node.left,node.val)
            rightMaxL = getMaxL(node.right,node.val)
            maxL = max(maxL, leftMaxL + rightMaxL)
            print('node=%s,maxL=%s'%(node.val,maxL))
            if node.val == val:
                return max(leftMaxL,rightMaxL) + 1
            else:
                return 0
        if root != None:
            getMaxL(root,root.val)
        return maxL


    def longestUnivaluePath1(self,root):
        res = 0
        def dns(node):
            nonlocal res
            if node == None:
                return 0
            lmax = dns(node.left)
            rmax = dns(node.right)
            if node.left and node.left.val == node.val:
                lmax = lmax + 1
            else:
                lmax = 0

            if node.right and node.right.val == node.val:
                rmax = rmax + 1
            else:
                rmax = 0
            res = max(res,lmax+rmax) #最大值是当前的最大值或者左右孩子路径的和。
            return max(lmax,rmax) #返回值是左右路径中的最大值,因为它还需要和父节点继续构建路径。

        dns(root)
        return res

root = TreeNode(1)
Node1 = TreeNode(4)
Node2 = TreeNode(5)
Node3 = TreeNode(5)
Node4 = TreeNode(4)
Node5 = TreeNode(5)
Node6 = TreeNode(5)

root.left = Node1
root.right = Node2
Node1.left = Node3
Node1.right = Node4
Node2.right = Node5
Node3.left = Node5
Node5.left = Node6

S = Solution()
res = S.longestUnivaluePath1(root)
print(res)

 

五、公共祖先

1.找出排序二叉树上任意两个节点的公共父节点

思考

  1. 两个节点的公共祖先一定在从根节点,至这两个节点的路径上;
  2. 由于求公共祖先中的最近公共祖先,那么即同时出现在这两条路径上的离根节点最远的节点(或离两个最近);
  3. 最终算法即:求p节点路径,q节点路径,两路径上最后一个相同的节点。

思路:先序遍历

  1. 从根节点遍历(搜索)至该节点,找到该节点后就结束搜索;
  2. 将遍历过程中遇到的节点按照顺序存储起来,这些节点即路径节点。
  3. 求出较短路径的长度n;
  4. 同时遍历p节点的路径和q节点的路径,遍历n个节点,最后一个发现的相同节点,即最近公共祖先

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

#获取二叉树从根节点root到node结点的路径
def getPathFromRoot(root,node,s):
    '''
    :param root:
    :param node: 二叉树中的某个节点
    :param s: 用来存储路径的栈
    :return: node在root的子树上
    '''
    if root == None:
        return False
    if root == node:
        s.append(root)
        return root
    if getPathFromRoot(root.left,node,s):
        s.append(root)
        return True
    elif getPathFromRoot(root.right,node,s):
        s.append(root)
        return True
    return False

#查找二叉树中两个节点最近的公共父节点
def FindParentNode(root,node1,node2):
    s1 = []
    path1 = getPathFromRoot(root,node1,s1)
    s2 = []
    path2 = getPathFromRoot(root,node2,s2)
    for ss1 in s1:
        print(ss1.val,end=',')
    print()
    for ss2 in s2:
        print(ss2.val,end=',')
    #找s1,s2第一个相等的节点,就是最近的公共父节点
    for i in s1:
        for j in s2:
            if i == j:
                return i.val

L1 = TreeNode(6)
L2 = TreeNode(3)
L3 = TreeNode(-7)
L4 = TreeNode(-1)
L5 = TreeNode(9)

L1.left = L2
L1.right = L3
L2.left = L4
L2.right = L5

res = FindParentNode(L1,L3,L4)
print('公共节点',res)

  

 

posted @ 2019-10-15 09:35  nxf_rabbit75  阅读(2524)  评论(0)    收藏  举报