文章目录
1 二叉树
1.1 二叉树递归 / 层序遍历
1.1.1 二叉树的 对称 / 翻转 / 镜像
3个入门级简单题,汇总一下:
class Solution:
def isSymmetric(self, root):
# 56ms 击败10%
# return self.check(root, root)
if root is None:
return True
# 52ms 击败20%
return self.check(root.left, root.right)
def check(self, p, q):
if p is None and q is None:
return True
if p is None or q is None:
return False
return p.val == q.val and self.check(p.left, q.right) and self.check(p.right, q.left)
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if root is None:
return None
root.right, root.left = self.invertTree(root.left), self.invertTree(root.right)
return root
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
def recur(node):
if node is None:
return None
node.left, node.right = recur(node.right), recur(node.left)
return node
return recur(root)
1.1.2 二叉树的直径 / 最大路径和
1.1.2.1 二叉树的直径
这种类型的题有两个关键步骤:
- 二叉树的直径
- 更新:经过当前结点的最大长度
- 返回:从当前结点出发的最大长度
- 二叉树中的最大路径和
- 更新:经过当前结点的最大路径和
- 返回:从当前结点出发的最大路径
有空重新刷一下,注意边界条件
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
ans = 1
def rec(node:TreeNode):
nonlocal ans
if node is None:
return 0
rl, rr = rec(node.left), rec(node.right)
# 更新:经过当前结点的最大长度
ans = max(ans, 1 + rl + rr)
# 返回:从当前结点出发的最大长度
return max(rl, rr) + 1
rec(root)
return ans - 1
1.1.2.2 二叉树中的最大路径和
class Solution:
def maxPathSum(self, root: TreeNode) -> int:
ans = -inf
def recursion(node)->int:
nonlocal ans
if node is None:
return 0
# 如果为负数,做一个截断
left = max(0, recursion(node.left))
right = max(0, recursion(node.right))
# 更新:经过当前结点的最大路径和
ans = max(ans, left + right + node.val)
# 返回:从当前结点出发的最大路径
return node.val + max(left, right)
recursion(root)
return ans
1.1.3 二叉树的最大 / 最小深度
最大深度
class Solution:
def maxDepth(self, root: TreeNode) -> int:
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1 if root else 0
最小深度
对于【二叉树的最小深度】,虽然BFS和DFS的时间复杂度相同,但一般来说BFS会更快,因为一般情况下只需要遍历部分结点即可返回
- DFS
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if root.left is None and root.right is None:
return 1
ans = inf
if root.left:
ans = min(ans, self.minDepth(root.left))
if root.right:
ans = min(ans, self.minDepth(root.right))
return ans + 1
- BFS
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root: return 0
q = [(root, 1)]
while q:
top, d = q.pop(0)
if top.left is None and top.right is None:
return d
if top.left:
q.append((top.left, d + 1))
if top.right:
q.append((top.right, d + 1))
1.1.4 填充每个节点的下一个右侧节点指针
- 递归
题目要求常数空间。
class Solution:
def connect(self, root: 'Node') -> 'Node':
if root is None:
return None
def recursion(node1, node2):
if node1 is None or node2 is None:
return
node1.next = node2
recursion(node1.left, node1.right)
recursion(node2.left, node2.right)
recursion(node1.right, node2.left)
recursion(root.left, root.right)
return root
- 层序遍历
跑起来快多了。。
class Solution:
def connect(self, root: 'Node') -> 'Node':
if root is None:
return None
queue = collections.deque()
queue.append(root)
while queue:
sz = len(queue)
pre = None
for _ in range(sz):
top = queue.popleft()
if pre:
pre.next = top
pre = top
if top.left:
queue.append(top.left)
if top.right:
queue.append(top.right)
return root
这题与上题不同,只能用BFS层序遍历求解了
1.1.5 二叉树的层序遍历
1.1.5.1 二叉树最大宽度
class Solution:
def widthOfBinaryTree(self, root: TreeNode) -> int:
q = [(root, 0)]
ans = 0
max_width = 0
while q:
sz = len(q)
lb, rb = inf, -inf
for i in range(sz):
top, idx = q.pop(0)
lb = min(lb, idx)
rb = max(rb, idx)
max_width = max(max_width, (rb - lb + 1))
if top.left:
q.append((top.left, idx * 2))
if top.right:
q.append((top.right, idx * 2 + 1))
return max_width
1.2 完全二叉树
1.2.1 复杂度 log 2 N \log^2N log2N的题目
这类题目有一个共同的特点:
- 每次遍历都需要计算一遍二叉树左右子树的高度(复杂度 log N \log N logN)
- 然后结合这个高度信息,再去遍历左右子树(复杂度 log N \log N logN)
二者叠加后,时间复杂度就变成了 log 2 N \log^2N log2N
1.2.1.1 完全二叉树的结点个数
算法笔记:如何计算完全二叉树的节点数
利用完全二叉树的性质:
- 如果
height_left == height_right
- 是满二叉树,用公式计算结点数
- 否则
- 用递归计算结点数
时间复杂度 O ( l o g 2 N ) O(log^2N) O(log2N)
class Solution:
def countNodes(self, root: TreeNode) -> int:
r = l = root
hr = hl = 0
while r is not None:
hr += 1
r = r.right
while l is not None:
hl += 1
l = l.left
# 空指针也会在这里返回
if hl == hr:
return 2 ** (hl) - 1
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
1.2.1.2 完全二叉树的最后一个节点
这题的lh
,rh
与上题的区别:
lh
:从左节点出发,一直往左走
rh
:从右结点出发,一直往右左走
用这张图简单说明一下思路:
- 如果
左子树高度>右子树高度
,目标结点必然在左子树 - 如果
左子树高度<=右子树高度
,目标结点必然在右子树
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def getLastNode(root: TreeNode) -> TreeNode:
if root is None or root.left is None:
return root
lp, rp = root.left, root.right
lh = rh = 0
while lp:
lp = lp.left
lh += 1
while rp:
rp = rp.left
rh += 1
if lh > rh:
return getLastNode(root.left)
elif lh <= rh:
return getLastNode(root.right)
if __name__ == '__main__':
case1 = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3, TreeNode(6)))
assert getLastNode(case1).val == 6
case2 = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3, TreeNode(6), TreeNode(7)))
assert getLastNode(case2).val == 7
case3 = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3))
assert getLastNode(case3).val == 5
case4 = TreeNode(1,
TreeNode(2, TreeNode(4, TreeNode(8), TreeNode(9)), TreeNode(5, TreeNode(10))),
TreeNode(3, TreeNode(6), TreeNode(7)))
assert getLastNode(case4).val == 1
1.2.2 完全二叉树的连续性性质
1.2.2.1 二叉树的完全性检验
class Solution:
def isCompleteTree(self, root: TreeNode) -> bool:
i = 0
nodes = [(root, 1)]
while i < len(nodes):
node, v = nodes[i]
i += 1
if node:
nodes.append([node.left, v * 2])
nodes.append([node.right, v * 2 + 1])
return len(nodes) == nodes[-1][1]
1.2.2.2 完全二叉树插入器
class CBTInserter:
def __init__(self, root: TreeNode):
self.root = root
queue = [root]
self.heap = []
while queue:
top = queue.pop(0)
self.heap.append(top)
if top.left:
queue.append(top.left)
if top.right:
queue.append(top.right)
def insert(self, v: int) -> int:
node = TreeNode(v)
self.heap.append(node)
n = len(self.heap)
pid = n // 2 - 1
parent = self.heap[pid]
if n % 2:
parent.right = node
else:
parent.left = node
return parent.val
def get_root(self) -> TreeNode:
return self.root
1.3 二叉搜索树(BST)
1.3.1. BST的增删改查判断
1.3.1.1. BST的查找 ⭐️
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
if root is None or root.val == val:
return root
if root.val < val:
return self.searchBST(root.right, val)
else:
return self.searchBST(root.left, val)
1.3.1.2. BST的插入 ⭐️⭐️
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if root is None:
return TreeNode(val)
if root.val < val:
root.right = self.insertIntoBST(root.right, val)
else:
root.left = self.insertIntoBST(root.left, val)
return root
1.3.1.3. BST的删除 ⭐️⭐️⭐️⭐️
- 叶子结点(0个孩子) → 当场去世
- 1个孩子 → 让孩子接替自己位置
- 两个孩子 → 找到
左子树中最大的结点
或 [右子树最小结点
] 接替自己
class Solution:
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if root is None:
return None
if root.val == key:
# == 情况1 == & == 情况2 ==
if root.left is None:
return root.right
if root.right is None:
return root.left
# == 情况3 ==
# 找到右子树的最小结点,即右子树不断往左遍历的最后一个结点
min_node = self.find_min(root.right)
# 【这个结点】和【待删除结点】做swap,但具体的操作形式为赋值:
# 【待删除结点】.val = 【右子树最小结点】.val
root.val = min_node.val
# 转化为子问题:删除【右子树最小结点】
root.right = self.deleteNode(root.right, min_node.val)
elif root.val > key: # 左右顺序写错
root.left = self.deleteNode(root.left