一、递归
一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。
1.1 树的高度
给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
- 递归法:
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
- 迭代法:
class Solution:
def maxDepth(self, root: TreeNode) -> int:
stack = []
if root is not None:
stack.append((1, root))
depth = 0
while stack != []:
current_depth, root = stack.pop()
if root is not None:
depth = max(depth, current_depth)
stack.append((current_depth + 1, root.left))
stack.append((current_depth + 1, root.right))
return depth
1.2 平衡树
给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
if not root: return True
return abs(self.depth(root.left) - self.depth(root.right)) <= 1 and \
self.isBalanced(root.left) and self.isBalanced(root.right)
def depth(self, root):
if not root: return 0
return max(self.depth(root.left), self.depth(root.right)) + 1
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
self.result = True
def maxDepth(root):
if not root:
return 0
l = maxDepth(root.left)
r = maxDepth(root.right)
if abs(l - r) > 1:
self.result = False
return 1 + max(l, r)
maxDepth(root)
return self.result
1.3 两结点的最长路径 / 二叉树的直径
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
Input:
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.max_val = 0
def depth(root):
if not root: return 0
left_depth = depth(root.left)
right_depth = depth(root.right)
self.max_val = max(self.max_val, left_depth + right_depth)
return max(left_depth, right_depth) + 1
depth(root)
return self.max_val
1.4 翻转树
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root: return None
root.left, root.right = self.invertTree(root.right), self.invertTree(root.left)
return root
1.5 归并两棵树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始。
class Solution:
def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
if not t1 and not t2: return None
if not t1: return t2
if not t2: return t1
root = TreeNode(t1.val + t2.val)
root.left = self.mergeTrees(t1.left, t2.left)
root.right = self.mergeTrees(t1.right, t2.right)
return root
1.6 判断路径和是否等于一个数
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if not root:
return False
if not root.left and not root.right and root.val == sum:
return True
return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)
1.7 统计路径和等于一个数的路径数量
给定一个二叉树,它的每个结点都存放着一个整数值。 找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 二叉树不超过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
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> int:
if not root: return 0
def pathSumStartWithRoot(root, sum):
if not root: return 0
ret = 0
if root.val == sum:
ret += 1
ret += pathSumStartWithRoot(root.left, sum - root.val) + pathSumStartWithRoot(root.right, sum - root.val)
return ret
ret = pathSumStartWithRoot(root, sum) + self.pathSum(root.left, sum) + self.pathSum(root.right, sum)
return ret
1.8 子树
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
示例 1:
给定的树 s:
3
/ \
4 5
/ \
1 2
给定的树 t:
4
/ \
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
示例 2:
给定的树 s:
3
/ \
4 5
/ \
1 2
/
0
给定的树 t:
4
/ \
1 2
返回 false。
class Solution:
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
if not s: return False
def isSubtreeWithRoot(s, t): # 树 s、t 是否完全相同
if not t and not s: return True
if not t or not s: return False
if t.val != s.val: return False
return isSubtreeWithRoot(s.left, t.left) and isSubtreeWithRoot(s.right, t.right)
return isSubtreeWithRoot(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)
1.9 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root: return True
def recur(t1, t2):
if not t1 and not t2: return True
if not t1 or not t2: return False
if t1.val != t2.val: return False
return recur(t1.left, t2.right) and recur(t1.right, t2.left)
return recur(root.left, root.right)
1.10 最小路径 / 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root: return 0
left = self.minDepth(root.left)
right = self.minDepth(root.right)
if left == 0 or right == 0: return left + right + 1
return min(left, right) + 1
1.11 统计左子节点的和
计算给定二叉树的所有左叶子之和。
示例:
3
/ \
9 20
/ \
15 7
在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
class Solution:
def sumOfLeftLeaves(self, root: TreeNode) -> int:
if not root: return 0
def isLeaf(node):
if not node: return False
return not node.left and not node.right
if isLeaf(root.left):
return root.left.val + self.sumOfLeftLeaves(root.right)
return self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right)
1.12 相同节点值得最大路径长度
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。 注意:两个节点之间的路径长度由它们之间的边数表示。
输入:
5
/ \
4 5
/ \ \
1 1 5
输出:
2
class Solution:
def longestUnivaluePath(self, root: TreeNode) -> int:
self.path = 0
def dfs(root):
if not root: return 0
left = dfs(root.left)
right = dfs(root.right)
left_path = left + 1 if root.left and root.left.val == root.val else 0
right_path = right + 1 if root.right and root.right.val == root.val else 0
self.path = max(self.path, left_path + right_path)
return max(left_path, right_path)
dfs(root)
return self.path
1.13 间隔遍历 / 打家劫舍(III)
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:
输入: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
对于每一个节点,都只有选和不选两种情况。我们每次考虑一棵子树,那么根只有两种情况,选和不选 (我们让 dp[0] 表示不选, dp[1] 表示选)。
class Solution:
def rob(self, root: TreeNode) -> int:
def dp(node):
if not node: return [0, 0]
l = dp(node.left)
r = dp(node.right)
# max(max(左不抢+右抢, 左抢+右不抢, 左不抢+右不抢, 左抢+右抢), 抢根节点+左不抢+右不抢)
# 等价为 max(max(左不抢,左抢) + max(右不抢,右抢),抢根节点+左不抢+右不抢)
return [max(l) + max(r), node.val + l[0] + r[0]]
return max(dp(root))
1.14 找出二叉树中最小的节点
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。
示例 1:
输入:
2
/ \
2 5
/ \
5 7
输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。
示例 2:
输入:
2
/ \
2 2
输出: -1
说明: 最小的值是 2, 但是不存在第二小的值。
一个节点要么具有 0 个或 2 个子节点,如果有子节点,那么根节点是最小的节点。
思路:寻找左右子树中的最小节点。
class Solution:
def findSecondMinimumValue(self, root: TreeNode) -> int:
if not root: return -1
if not root.left and not root.right: return -1
left_val = root.left.val
right_val = root.right.val
if left_val == root.val: # 说明 left_val 不可能为第二小的节点,需要去找左子树中最小节点
left_val = self.findSecondMinimumValue(root.left)
if right_val == root.val: # 说明 right_val 不可能为第二小的节点,需要去找右子树中最小节点
right_val = self.findSecondMinimumValue(root.right)
if left_val != -1 and right_val != -1: # 均不为 -1 说明,说明左右子树均有最小节点
return min(left_val, right_val)
if left_val != -1: # 说明 right_val = -1,右子树无最小节点,第二小节点为左子树的最小节点
return left_val
return right_val # 说明 left_val = -1,左子树无最小节点,第二小节点为右子树的最小节点
二、层次遍历
使用 BFS 进行层次遍历。不需要使用两个队列来分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
2.1 一棵树每层节点的平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例 1:
输入:
3
/ \
9 20
/ \
15 7
输出: [3, 14.5, 11]
解释:
第0层的平均值是 3, 第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].
class Solution:
def averageOfLevels(self, root: TreeNode) -> List[float]:
ret = []
if not root: return ret
queue = collections.deque()
queue.append(root)
while queue:
cnt = len(queue)
sum = 0.0
for _ in range(cnt): # 求本层所有节点的和,并将下层左右节点添加到队列中
node = queue.popleft()
sum += node.val
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
ret.append(sum / cnt)
return ret
2.2 得到左下角的节点
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:
输入:
2
/ \
1 3
输出:
1
示例 2:
输入:
1
/ \
2 3
/ / \
4 5 6
/
7
输出:
7
class Solution:
def findBottomLeftValue(self, root: TreeNode) -> int:
queue = collections.deque()
queue.append(root)
while queue: # 一层层的寻找
root = queue.popleft()
if root.right: queue.append(root.right)
# 后添加左节点,则左节点后出来,当最后一层左右节点均存在时,左节点为 root
if root.left: queue.append(root.left)
return root.val
三、前中后序遍历
1
/ \
2 3
/ \ \
4 5 6
- 层次遍历顺序:[1 2 3 4 5 6]
- 前序遍历顺序:[1 2 4 5 3 6]
- 中序遍历顺序:[4 2 5 1 3 6]
- 后序遍历顺序:[4 5 2 6 3 1]
层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;而前序、中序、后序遍历利用了 DFS 实现。
前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。
① 前序:根左右
def preorder(root):
if root:
print(root.val)
preorder(root.left)
preorder(root.right)
② 中序:左根右
def inorder(root):
if root:
inorder(root.left)
print(root.val)
inorder(root.right)
③ 后序:左右根
def postorder(root):
if root:
postorder(root.left)
postorder(root.right)
print(root.val)
3.1 非递归实现二叉树的前序遍历
给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
ret = []
stack = [root]
while stack:
node = stack.pop()
if not node: continue
ret.append(node.val) # 根左右
stack.append(node.right) # 先右后左,保证左子树先遍历
stack.append(node.left)
return ret
3.2 非递归实现二叉树后序遍历
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
ret = []
stack = [root]
while stack:
node = stack.pop()
if not node: continue
ret.append(node.val)
stack.append(node.left)
stack.append(node.right)
return ret[::-1]
3.3 非递归实现二叉树的中序遍历
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
ret = []
if not root: return ret
stack = []
cur = root
while cur or stack:
while cur:
stack.append(cur)
cur = cur.left
node = stack.pop()
ret.append(node.val)
cur = node.right
return ret
四、BST (Binary Search Tree)
二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。
二叉查找树中序遍历有序。
4.1 修剪二叉查找树
给定一个二叉搜索树,同时给定最小边界 L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在 [L, R] 中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
输入:
1
/ \
0 2
L = 1
R = 2
输出:
1
\
2
- 当 node.val > R,那么修剪后的二叉树必定出现在节点的左边
- 当 node.val < L,那么修剪后的二叉树出现在节点的右边
- 否则,我们将会修剪树的两边。
class Solution:
def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode:
def trim(node):
if not node:
return None
elif node.val > R: # 修剪后的二叉树必定出现在节点的左边
return trim(node.left)
elif node.val < L: # 修剪后的二叉树出现在节点的右边
return trim(node.right)
else: # 修剪树的两边
node.left = trim(node.left)
node.right = trim(node.right)
return node
return trim(root)
4.2 寻找二叉查找树的第 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
使用中序遍历:
- 递归法:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
def inorder(root):
return inorder(root.left) + [root.val] + inorder(root.right) if root else []
return inorder(root)[k - 1]
- 迭代法
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
stack = []
while True:
while root:
stack.append(root)
root = root.left
root = stack.pop()
k -= 1
if k == 0:
return root.val
root = root.right
4.3 把二叉查找树每个节点的值都加上比它大的节点的值
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
输入: 原始二叉搜索树:
5
/ \
2 13
输出: 转换为累加树:
18
/ \
20 13
先遍历右子树:
class Solution:
def convertBST(self, root: TreeNode) -> TreeNode:
self.sum = 0
def traver(node):
if not node: return
traver(node.right)
self.sum += node.val
node.val = self.sum
traver(node.left)
traver(root)
return root
4.4 二叉查找树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
_______6______
/ \
___2__ ___8__
/ \ / \
0 4 7 9
/ \
3 5
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
- 递归法:
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root.val > p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left, p, q)
elif root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right, p, q)
else:
return root
- 迭代法:
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while root:
if root.val > p.val and root.val > q.val:
root = root.left
elif root.val < p.val and root.val < q.val:
root = root.right
else:
return root
4.5 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
_______3______
/ \
___5__ ___1__
/ \ / \
6 2 0 8
/ \
7 4
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 1、终止条件:
# root 为空时,直接返回空,即返回 root
# root 等于 p 或 q 时,直接返回 root
if not root or root == p or root == q:
return root
# 2、地推工作
# 开启递归左子节点,返回值记为 left
# 开启递归右子节点,返回值记为 right
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
# 3、返回值:
if not left: # left 不为空
return right
if not right: # right 不为空
return left
return root # 同时不为空
返回值: 根据 left 和 right ,可展开为四种情况;
- 当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q ,返回 null;
- 当 left 和 right **同时不为空 **:说明 p, q 分别在 root 的 异侧 (分别在 左 / 右子树),因此 root 为最近公共祖先,返回 root ;
- 当 left 为空 ,right 不为空 :p,q 都不在 root 的左子树中,直接返回 right 。具体可分为两种情况:
- p,q 其中一个在 root 的 右子树 中,此时 right 指向 p(假设为 p );
- p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
- 当 left 不为空 , right 为空 :与情况 3. 同理;
4.6 从有序数组中构造二叉查找树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
右边: 0 左边: 0
/ \ / \
-3 9 -10 5
/ / \ \
-10 5 -3 9
- 始终选择中间位置左边元素作为根结点
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def helper(left, right):
if left > right:
return None
# always choose left middle node as a root
p = (left + right) // 2
# inorder traversal: left -> node -> right
root = TreeNode(nums[p])
root.left = helper(left, p - 1)
root.right = helper(p + 1, right)
return root
return helper(0, len(nums) - 1)
- 始终选择中间位置右边元素作为根结点
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def helper(left, right):
if left > right:
return None
# always choose right middle node as a root
p = (left + right) // 2
if (left + right) % 2:
p += 1
# inorder traversal: left -> node -> right
root = TreeNode(nums[p])
root.left = helper(left, p - 1)
root.right = helper(p + 1, right)
return root
return helper(0, len(nums) - 1)
4.7 根据有序链表构造平衡的二叉查找树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
class Solution:
def sortedListToBST(self, head: ListNode) -> TreeNode:
self.temp = []
while head:
self.temp.append(head.val)
head = head.next
def BST(left, right):
if left > right: return
mid = left + (right - left) // 2
root = TreeNode(self.temp[mid])
root.left = BST(left, mid - 1)
root.right = BST(mid + 1, right)
return root
return BST(0, len(self.temp) - 1)
- 使用递归模拟
中序遍历
过程,建立节点的顺序即与链表元素顺序一一对应,bottom-up
(自底向上) 建立树,最终返回根节点。
class Solution:
def sortedListToBST(self, head: ListNode) -> TreeNode:
self.head = head
n = 0
while head:
head = head.next
n += 1
def toBST(left, right):
if left > right: return
mid = left + (right - left) // 2
node_left = toBST(left, mid - 1)
root = TreeNode(self.head.val)
self.head = self.head.next
root.left = node_left
root.right = toBST(mid + 1, right)
return root
return toBST(0, n - 1)
4.8 在二叉查找树中寻求两个节点,使它们的和为一个给定值
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
输入:
5
/ \
3 6
/ \ \
2 4 7
Target = 9
输出: True
使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。
应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。
class Solution:
def findTarget(self, root: TreeNode, k: int) -> bool:
self.temp = []
def inorder(root):
if root:
inorder(root.left)
self.temp.append(root.val)
inorder(root.right)
inorder(root)
l, r = 0, len(self.temp) - 1
while l < r:
if self.temp[l] + self.temp[r] == k:
return True
elif self.temp[l] + self.temp[r] > k:
r -= 1
else:
l += 1
return False
4.9 在二叉查找树中查找两个节点之差的最小绝对值
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
输入:
1
\
3
/
2
输出:1
解释:最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。
- 利用中序遍历将二叉树转换为有序的数组
class Solution:
def getMinimumDifference(self, root: TreeNode) -> int:
self.temp = []
def inorder(root):
if root:
inorder(root.left)
self.temp.append(root.val)
inorder(root.right)
inorder(root)
minval = float("inf")
for i in range(1, len(self.temp)):
minval = min(minval, self.temp[i] - self.temp[i - 1])
return minval
class Solution:
def getMinimumDifference(self, root: TreeNode) -> int:
def preorder(root):
if not root:
return []
else:
return preorder(root.left) + [root.val] + preorder(root.right)
num = preorder(root)
min_ = float('inf')
for i in range(len(num) - 1):
min_ = min(min_, num[i+1] - num[i])
return min_
- 利用二叉查找树的中序遍历为有序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。
class Solution:
def getMinimumDifference(self, root: TreeNode) -> int:
self.min_val = float("inf")
self.pre_node = None
def inorder(root):
if root:
inorder(root.left)
if self.pre_node:
self.min_val = min(self.min_val, root.val - self.pre_node.val)
self.pre_node = root
inorder(root.right)
inorder(root)
return self.min_val
4.10 寻找二叉查找树中出现次数最多的值
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。 假定 BST 有如下定义:
结点左子树中所含结点的值小于等于当前结点的值 结点右子树中所含结点的值大于等于当前结点的值 左子树和右子树都是二叉搜索树
给定 BST [1,null,2,2],
1
\
2
/
2
返回[2].
答案可能不止一个,也就是有多个值出现的次数一样多。
class Solution:
def findMode(self, root: TreeNode) -> List[int]:
if not root: return []
self.table = {}
def inorder(root):
if root:
inorder(root.left)
if root.val not in self.table:
self.table[root.val] = 1
else:
self.table[root.val] += 1
inorder(root.right)
inorder(root)
res = []
m = max(self.table.values())
for k, v in self.table.items():
if v == m:
res.append(k)
return res