目录
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
题解
一:DFS,递归。
class Solution(object):
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: List[str]
"""
res, rec = [], ""
if not root:
return res
self._helper(root, rec, res)
return res
def _helper(self, node, rec, res):
if not node.left and not node.right:
res.append(rec+str(node.val))
return
if node.left:
self._helper(node.left, rec+str(node.val)+"->", res)
if node.right:
self._helper(node.right, rec+str(node.val)+"->", res)
113. 路径总和 II
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
题解
一:DFS,递归,感觉目前遇到的树类型的题目思路都差不多。需要记录值,最后还要释放,释放的时候由于python是引用,所以值拷贝放入的最终结果,看代码。
class Solution(object):
def pathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: List[List[int]]
"""
res, rec = [], []
if not root:
return res
self._helper(root, rec, sum, res)
return res
def _helper(self, node, rec, target, res):
if not node.left and not node.right:
if target == node.val:
rec.append(node.val)
res.append(rec[:])
rec.pop()
return
rec.append(node.val)
if node.left:
self._helper(node.left, rec, target - node.val, res)
if node.right:
self._helper(node.right, rec, target - node.val, res)
rec.pop()
129. 求根到叶子节点数字之和
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。例如,从根到叶子节点路径 1->2->3 代表数字 123。计算从根到叶子节点生成的所有数字之和。说明: 叶子节点是指没有子节点的节点。
示例 1:输入: [1,2,3]
1
/ \
2 3
输出: 25
解释:从根到叶子节点路径 1->2 代表数字 12。从根到叶子节点路径 1->3 代表数字 13。因此,数字总和 = 12 + 13 = 25。
示例 2:输入: [4,9,0,5,1]
4
/ \
9 0
/ \
5 1
输出: 1026
解释:从根到叶子节点路径 4->9->5 代表数字 495。从根到叶子节点路径 4->9->1 代表数字 491。从根到叶子节点路径 4->0 代表数字 40。因此,数字总和 = 495 + 491 + 40 = 1026。
题解
一:大体相同的思路,这边接住了库函数,str和int。
class Solution(object):
def sumNumbers(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
res, rec = [0], 0
self._helper(root, res, rec)
return res[0]
def _helper(self, node, res, rec):
if not node.left and not node.right:
res[0] = res[0] + rec * 10 + node.val
return
rec = rec * 10 + node.val
if node.left:
self._helper(node.left, res, rec)
if node.right:
self._helper(node.right, res, rec)
437. 路径总和 III
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过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
题解
一:起始点的三种情况:当前节点,当前节点的左孩子,当前节点的右孩子,self._helper(root, sum, res),以当前节点的左右孩子(即不包括当前节点)做起始点,这么看,只是缺少了以根节点作为起始节点的情况,故单独给出。
class Solution(object):
def pathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: int
"""
if not root:
return 0
# 以root为根的符合题目要求的路径数
self._sum(root, sum, res)
# 以root的左右孩子为根的符合题目要求的路径数
self._helper(root, sum, res)
return res[0]
# 递归遍历每一个节点,看看他们左右孩子为根的符合题目要求的路径数
def _helper(self, node, target, res):
if not node:
return
self._sum(node.left, target, res)
self._sum(node.right, target, res)
self._helper(node.left, target, res)
self._helper(node.right, target, res)
# 以node为根的符合题目要求的路径数,必须以node为起点
def _sum(self, node, target, res):
if not node:
return
target -= node.val
if target == 0:
res[0] += 1
if node.left:
self._sum(node.left, target, res)
if node.right:
self._sum(node.right, target, res)
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8。输出: 6 。解释: 节点 2 和节点 8 的最近公共祖先是 6。
题解
一:二叉搜索树,是一个二叉树(不一定是一个完全的二叉树),每个节点的键值大于左孩子;每个节点的键值小于右孩子;以左右孩子为根的子树仍为二分搜索树,通过DFS,若我们遇到的节点的键值位于p和q键值的中间,这该节点就是最近的公共祖先,若小于p和q的最小值,则其最近的公共祖先必在其右子树的节点上,反之若大于p和q的最大值,则其最近的公共祖先必在其左子树的节点上。
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root:
return None
if p.val > q.val:
p, q = q, p
return self._helper(root, p, q)
def _helper(self, node, p, q):
if not node:
return None
if p.val <= node.val <= q.val:
return node
if node.val > q.val:
return self._helper(node.left, p, q)
if node.val < p.val:
return self._helper(node.right, p, q)
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false。解释: 输入为: [5,1,4,null,null,3,6]。根节点的值为 5 ,但是其右子节点值为 4 。
题解
一:暴力法,时间复杂度O(n^2),对每一个节点,查看其左子树是否都小于该节点,查看右子树是否都大于该节点
二:中序遍历法,时间复杂度O(n),将树中序遍历的值存起来,看看是否是严格升序
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
rec = []
self._in_order(root, rec)
for i in range(1, len(rec)):
if rec[i - 1] >= rec[i]:
return False
return True
def _in_order(self, node, rec):
if not node:
return
if node.left:
self._in_order(node.left, rec)
rec.append(node.val)
if node.right:
self._in_order(node.right, rec)
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。一般来说,删除节点可分为两个步骤:首先找到需要删除的节点;如果找到了,删除它。说明: 要求算法时间复杂度为 O(h),h 为树的高度。
示例:root = [5,3,6,2,4,null,7],key = 3
5
/ \
3 6
/ \ \
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/ \
4 6
/ \
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
题解
一:如果要删除的元素没有左子树,那么用该元素的右节点替换掉该元素即可;
如果要删除的元素没有右子树,那么用该元素的左节点替换掉该元素即可;
如果要删除的元素左右子树都存在,那么可以拿右子树的最左边的节点(后继)代替删除元素或者拿左子树的最右边的节点(左子树中的最大值)代替删除元素。
class Solution(object):
def deleteNode(self, root, key):
"""
:type root: TreeNode
:type key: int
:rtype: TreeNode
"""
if not root:
return root
return self._deleteNode(root, key)
# 返回删除节点后的根
def _deleteNode(self, node, key):
if not node:
return node
if node.val < key:
# return self._deleteNode(node.right, key)
node.right = self._deleteNode(node.right, key)
# 注意将跟节点返回回去
return node
elif node.val > key:
# return self._deleteNode(node.left, key)
node.left = self._deleteNode(node.left, key)
# 注意将跟节点返回回去
return node
else:
# node
# 没有左子树
if not node.left:
return node.right
# 没有右子树
if not node.right:
return node.left
# 既有左孩子又有右孩子
if node.right and node.left:
new_root_node = self._successor(node.right)
# 删除其后继
new_root_node.right = self._deleteNode(node.right, new_root_node.val)
new_root_node.left = node.left
return new_root_node
def _successor(self, node):
while node.left:
node = node.left
return node
108. 将有序数组转换为二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
题解
一:DFS深度遍历,构造一棵树的过程可以拆分成无数个这样的子问题:构造树的每个节点以及节点之间的关系。对于每个节点来说,都需要:选取根节点(因为要平衡,选取数组的中间值作为根),构造该节点的左子树,构造该节点的右子树
- 选择中间元素构建根节点node
- 构建左子树的根节点(左半部分的中间元素)
- 构建右子树的中间节点(右半部分的中间元素)
- 返回根节点
class Solution(object):
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
def helper(l, r, nums):
mid = (r - l) // 2 + l
root = None
if mid < 0 or mid >= len(nums) or l > r:
return None
root = TreeNode(nums[mid])
root.right = helper(mid + 1, r, nums)
root.left = helper(l, mid - 1, nums)
return root
if not nums:
return None
return helper(0, len(nums) - 1 , nums)
230. 二叉搜索树中第K小的元素
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。说明:你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
示例 1:输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 1
示例 2:输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
输出: 3
进阶:
如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数?
题解
一:借用中序遍历的思想,将中序遍历的结果存入rec,则rec是升序的,返回rec[k-1]即可。
class Solution(object):
def kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
rec = []
self._helper(root, rec)
return rec[k-1]
def _helper(self, node, rec):
if node:
if node.left:
self._helper(node.left, rec)
rec.append(node.val)
if node.right:
self._helper(node.right, rec)
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1,输出: 3,解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4,输出: 5,解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉树中。
题解
一:先考察p和q是否是最近公共祖先,再考虑公共祖先不是p和q的情况,这个时候p和q必定分别位于左子树和右子树。
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
res = [root]
if self._exist(p, q):
return p
if self._exist(q, p):
return q
self._recursive(root, p, q, res)
return res[0]
def _recursive(self, node, p, q, res):
if not node:
return False
if p.val != node.val and q.val != node.val:
if (self._exist(node.left, p) and self._exist(node.right, q)) \
or (self._exist(node.left, q) and self._exist(node.right, p)):
res[0] = node
return True
return self._recursive(node.left, p, q, res) or self._recursive(node.right, p, q, res)
def _exist(self, root, node):
if not root:
return False
if root.val == node.val:
return True
return self._exist(root.left, node) or self._exist(root.right, node)
二:转自官方题解,力扣,
这种方法非常直观。先深度遍历该树。当你遇到节点 p 或 q 时,返回一些布尔值作为标记。标记有助于确定是否在任何路径中找到了所需的节点。最不常见的祖先将是两个子树递归都返回 true 标记的节点。它也可以是一个节点,它本身是 p 或 q 中的一个,对于这个节点,子树递归返回一个 true 标记。
让我们看看基于这个想法的正式算法。
算法:
- 从根节点开始遍历树。
- 如果当前节点本身是 p 或 q 中的一个,我们会将变量 mid 标记为 true,并继续搜索左右分支中的另一个节点。
- 如果左分支或右分支中的任何一个返回 true,则表示在下面找到了两个节点中的一个。
- 如果在遍历的任何点上,left、right 或者 mid 三个标记中的任意两个变为 true,这意味着我们找到了节点 p 和 q 的最近公共祖先。
让我们看一个示例,搜索树中两个节点 9 和 11 的最近公共祖先。
1 --> 2 --> 4 --> 8
回溯 8 --> 4
4 --> 9(找到一个节点,返回 true )
回溯 9 --> 4 --> 2
2 --> 5 --> 10
回溯 10 --> 5
5 --> 11(找到另一个节点,返回 true )
回溯 --> 5 --> 2在节点 2 这里出现 left = true 且 right = true,因此节点 2 是它们的最近公共祖先。
时间复杂度:O(N) ,N 是二叉树中的节点数,最坏情况下,我们需要访问二叉树的所有节点。
空间复杂度:O(N) ,这是因为递归栈使用的最大空间位 N ,斜二叉树的高度可以是 N 。
class Solution(object):
def __init__(self):
self.ans = None
def lowestCommonAncestor(self, root, p, q):
def recurse_tree(node):
if not node:
return False
left = recurse_tree(node.left)
right = recurse_tree(node.right)
mid = (node.val == p.val or node.val == q.val)
if mid + left + right >= 2:
self.ans = node
return mid or left or right
recurse_tree(root)
return self.ans
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root:
return root
if root.val == p.val or root.val == q.val:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root
elif left:
return left
else:
return right