一、二叉搜索树的最近公共祖先
此题为leetcode第235题
思路:因为此题的前提是二叉搜索树,有个很好的性质即根节点的左子树的值都小于根节点,右子树的值都大于根节点。因此给定两个值p和q,如果p和q都小于根节点的话说明他们一定在左子树,继续在左子树寻找,对于右子树也是如此;如果p和q有一个小于等于根节点而另一个大于等于根节点,说明当前根节点便是最近公共祖先。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 递归
# 当p、q的值都小于root时,要在左子树找
if p.val < root.val and q.val < root.val:
return self.lowestCommonAncestor(root.left, p, q)
# 当p、q的值都大于root时,要在右子树找
if p.val > root.val and q.val > root.val:
return self.lowestCommonAncestor(root.right, p, q)
# 否则p或q分列左右子树,root即为最近公共祖先
return root
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 迭代
while root:
if p.val < root.val and q.val < root.val:
root = root.left
elif p.val > root.val and q.val > root.val:
root = root.right
else:
return root
二、二叉树的最近公共祖先
此题为leetcode第236题
思路:设left和right分别表示节点x的左右子树是否包含节点p或q,如果包含为True,否则为False。那么符合条件的最近公共祖先节点x满足以下条件:
( l e f t & & r i g h t ) ∥ ( ( x = = p ∥ x = = q ) & & ( l e f t ∥ r i g h t ) ) (left \quad\&\& \quad right) \quad ∥ \quad ((x==p ∥x==q) \quad \&\& \quad (left ∥right)) (left&&right)∥((x==p∥x==q)&&(left∥right))
对于第一个条件,说明左子树和右子树均包含p节点或q节点,如果左子树包含的是p节点,那么右子树只能包含q节点,反之亦然,因为p节点和q节点都是不同且唯一的节点,因此如果满足这个判断条件即可说明x就是我们要找的最近公共祖先。对于第二个条件,即是考虑了x恰好是p节点或q节点且它的左子树或右子树有一个包含了另一个节点的情况,因此如果满足这个判断条件亦可说明x就是我们要找的最近公共祖先。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
def helper(root, p, q):
if root is None:
return False
left = helper(root.left, p, q)
right = helper(root.right, p, q)
if (left and right) or ((root.val == p.val or root.val == q.val) and (left or right)):
self.res = root
return left or right or (root.val == p.val or root.val == q.val)
self.res = None
helper(root, p, q)
return self.res
三、最深叶节点的最近公共祖先
此题为leetcode第1123题
思路:最深的叶节点可能有多个,如果只有一个最深叶节点的话,那么最近公共祖先就是它自己。我们发现最近公共祖先的左右子树都是等高的,如果不等高,那么高度较小的那个子树的叶节点就不是最深的。我们递归地解决该问题,每次递归返回两个值:一是当前节点子树中最深叶节点的最近公共祖先,另一个是当前节点的深度。然后比较:(1)如果左子树的深度大于右子树的深度,说明最深叶节点在左子树中,那么返回左子树的最深叶节点的最近公共祖先和当前节点的深度;(2)如果左子树的深度小于右子树的深度,说明最深叶节点在右子树中,那么返回右子树的最深叶节点的最近公共祖先和当前节点的深度;(3)如果左右子树的深度相等,那么返回当前节点和当前节点的深度。
class Solution:
def lcaDeepestLeaves(self, root: TreeNode) -> TreeNode:
def helper(root):
if not root:
return None, 0
left_node, left_depth = helper(root.left)
right_node, right_depth = helper(root.right)
if left_depth < right_depth:
return right_node, right_depth + 1
elif left_depth > right_depth:
return left_node, left_depth + 1
else:
return root, left_depth + 1
return helper(root)[0]
四、二叉树的直径
此题为leetcode第543题
思路:假设我们知道对于该节点的左儿子向下遍历经过最多的节点数L(即以左儿子为根的子树的深度) 和其右儿子向下遍历经过最多的节点数R(即以右儿子为根的子树的深度),那么以该节点为起点的路径经过节点数的最大值即为 L + R + 1。我们记节点node为起点的路径经过节点数的最大值为d,那么二叉树的直径就是所有节点d的最大值减一。
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.res = 1
self.DFS(root)
return self.res - 1
def DFS(self, node):
if node is None:
return 0
L = self.DFS(node.left) # 左子树的深度
R = self.DFS(node.right) # 右子树的深度
self.res = max(self.res, L + R + 1)
return max(L, R) + 1 # 返回以该节点为子树的深度
五、对称二叉树
此题为leetcode第101题
思路:对于递归,我们递归的比较左子树left和右子树right,如果left和right均为None,那么返回True;如果left和right只有一个为None,那么返回False;如果left的值不等于right的值,那么返回False;然后递归地比较left的左子树和right的右子树,然后比较left的右子树和right的左子树。对于迭代,从队列中取出left和right,如果left和right均为None,那么continue;如果只有一个为None,那么返回False;如果left的值不等于right的值,那么返回False;然后按照下面的顺序依次入队节点:left.left,right.right,left.right,right.left
# 递归
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if root is None:
return True
def helper(left_node, right_node):
if left_node is None and right_node is None:
return True
if left_node is None or right_node is None:
return False
if left_node.val != right_node.val:
return False
return helper(left_node.left, right_node.right) and helper(left_node.right, right_node.left)
return helper(root.left, root.right)
# 迭代
from collections import deque
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if root is None:
return True
q = deque()
q.append(root)
q.append(root)
while q:
node1 = q.pop()
node2 = q.pop()
if node1 is None and node2 is None:
continue
if node1 is None or node2 is None:
return False
if node1.val != node2.val:
return False
if node1.val == node2.val:
q.append(node1.left)
q.append(node2.right)
q.append(node1.right)
q.append(node2.left)
return True
六、翻转二叉树
此题为leetcode第226题
思路:可以用递归或迭代去解决。对于递归,在当前节点root下,我们先递归地返回翻转好的root.left和root.right,并将它们交换,然后返回root。对于迭代,类似于BFS、层序遍历,当前循环下,队列出队一个节点node,交换node.left和node.right,然后依次将node.left和node.right入队。
# 递归
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
if root is None:
return root
left = self.mirrorTree(root.left)
right = self.mirrorTree(root.right)
root.left, root.right = right, left
return root
# 迭代
from collections import deque
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
if root is None:
return root
q = deque()
q.append(root)
while q:
node = q.popleft()
node.left, node.right = node.right, node.left
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return root
七、二叉树的最大深度
此题为leetcode第104题
思路:递归方法:我们可以将当前节点的左右子树分别传入下层递归,并返回他们的最大深度left_depth和right_depth,他们取最大值并加1即为当前节点下的最大深度。迭代:类似于层序遍历,队列入队当前节点和深度,循环的时候依次弹出,和上层循环已经保存的最大深度对比取最大者,然后左右子树及其深度入队。
# 递归
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
left_depth = self.maxDepth(root.left) # 左子树深度
right_depth = self.maxDepth(root.right) # 右子树深度
return max(left_depth, right_depth) + 1
# 迭代
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
depth = 0
q = deque()
q.append((root, 1))
while q:
node, curr_depth = q.popleft()
depth = max(depth, curr_depth)
if node.left:
q.append((node.left, curr_depth + 1))
if node.right:
q.append((node.right, curr_depth + 1))
return depth
八、二叉树锯齿形层序遍历
此题为leetcode第103题
思路:对于BFS,在每层遍历结束后,按照层是奇数还是偶数,来判断是顺序append还是逆序append。对于DFS来说也是,可以每层设置一个队列,是偶数层就deque.append,是奇数层就deque.appendleft。注意最后将答案里的队列转为列表。
# BFS
from collections import deque
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if root is None:
return []
q = deque()
q.append(root)
res = []
level = 1
while q:
size = len(q)
temp = []
for i in range(size):
node = q.popleft()
temp.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
if level % 2 == 1:
res.append(temp)
else:
res.append(temp[::-1])
level += 1
return res
# DFS
from collections import deque
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
def helper(node, level):
if level >= len(res):
res.append(deque([node.val]))
else:
if level % 2 == 0:
res[level].append(node.val)
else:
res[level].appendleft(node.val)
if node.left:
helper(node.left, level + 1)
if node.right:
helper(node.right, level + 1)
if root is None:
return []
res = []
helper(root, 0)
return [list(a) for a in res]
九、另一个树的子树
leetcode第572题
思路:遍历s的每个节点,然后判断s的每个子树是否包含和t具有相同结构和节点的子树。
class Solution:
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
if s is None or t is None:
return False
stack = [s]
while stack:
node = stack.pop()
if node is not None:
if node.val == t.val and self.is_equal(node, t):
return True
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return False
def is_equal(self, node1, node2):
if node2 is None and node1 is not None:
return False
if node1 is None and node2 is None:
return True
if node1 is None or node1.val != node2.val:
return False
return self.is_equal(node1.left, node2.left) and self.is_equal(node1.right, node2.right)
十、路径总和
此题为leetcode第112题
思路:递归地判断路径总和是否等于目标值。如果遇到叶子节点,此时路径总和为目标值则返回true,否则返回false
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if root is None:
return False
self.sum = sum
return self.dfs(root, 0)
def dfs(self, root, path):
if root is None:
return False
path = path + root.val
if root.left is None and root.right is None:
return path == self.sum
return self.dfs(root.left, path) or self.dfs(root.right, path)
十一、路径总和II
此题为leetcode第113题
思路:和上一题类似,在递归的过程中如果遇到等于目标值的路径,就将其放入self.res中。
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
if root is None:
return []
self.res = []
self.sum = sum
self.dfs(root, 0, [])
return self.res
def dfs(self, root, curr_sum, path):
if root is None:
return None
if root.left is None and root.right is None and curr_sum + root.val == self.sum:
self.res.append(path + [root.val])
self.dfs(root.left, curr_sum + root.val, path + [root.val])
self.dfs(root.right, curr_sum + root.val, path + [root.val])
未完待续