6.2 数据结构---树(路径)
一、路径
1.二叉树的所有路径 leetcode 257
思路:深度遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 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)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 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 ] ] |
思路:
- 从根节点深度遍历二叉树,先序遍历时,将该节点值存储值path栈中,使用path_value累加节点值;
- 当遍历至叶节点时,检查path_value值是否为sum,若为sum,则将path push进入result中;
- 在后续遍历时,将该节点值从path栈中弹出,path_value减去节点值。
代码1:用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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.当前节点+右子树🌲
这三种情况的最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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返回给父节点。其中,可以把最长路径保存到一个全局变量中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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.找出排序二叉树上任意两个节点的公共父节点
思考:
- 两个节点的公共祖先一定在从根节点,至这两个节点的路径上;
- 由于求公共祖先中的最近公共祖先,那么即同时出现在这两条路径上的离根节点最远的节点(或离两个最近);
- 最终算法即:求p节点路径,q节点路径,两路径上最后一个相同的节点。
思路:先序遍历
- 从根节点遍历(搜索)至该节点,找到该节点后就结束搜索;
- 将遍历过程中遇到的节点按照顺序存储起来,这些节点即路径节点。
- 求出较短路径的长度n;
- 同时遍历p节点的路径和q节点的路径,遍历n个节点,最后一个发现的相同节点,即最近公共祖先。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现