算法题刷题笔记-二叉树
算法题刷题笔记-二叉树
1. 二叉树基础:
- 一些概念:
- 满二叉树:深度为k的满二叉树必须满足节点总数为$2^k-1$,且第i层的节点数达到最大值$2^{i-1}$
- 完全二叉树:所有非最后一层节点均有两个子节点,且最后一层节点连续集中在左侧。(从上到下,从左到右,结点编号连续)
- 二叉搜索树:左子树的所有节点都小于中间节点,右子树的所有节点都大于中间节点。(搜索一个节点的时间复杂度是logn)
- 平衡二叉搜索树:左子树和右子树的高度差的绝对值不大于1。(map、set、multimap和multiset的底层数据结构都是平衡二叉搜索树,它们插入或检索一个元素的时间复杂度都是logn;unordered_map和unordered_set底层是哈希表)
- 储存方式:
- 链式存储:二叉链表,每个节点类一般有三个对象(left,val和right),分别对应的是该节点的左孩子节点、节点值和右孩子节点。
1
2
3
4
5class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right - 线性存储:数组,元素i的左孩子节点下标是2i+1,右孩子节点下标是2i+2
- 遍历方式:
- DFS和BFS:前中后序遍历是DFS,层序遍历是BFS;方法可以是队列递归或队列迭代
- 前中后序主要是看根节点访问的先后次序:前序是中左右,中序是左中右,后序是左右中
2. 二叉树的中序遍历(LC94)
1 | class Solution: |
- 最基础的中序遍历,遍历顺序是左中右。
3. 二叉树的最大深度(LC104)
- 自顶向下 - 前序遍历
- 自底向上 - 后序遍历
- 自底向上比较反直觉,但很符合递归写法
1
2
3
4
5
6
7
8
9
10
11
12class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
# 自底向上 -> 后序遍历左右中
# 终止条件
if root is None:
return 0
# 左子树高度
left_height = self.maxDepth(root.left)
# 右子树高度
right_height = self.maxDepth(root.right)
# 中间节点处理逻辑
return 1 + max(left_height, right_height) - 自顶向下符合构造二叉树的直觉(从根节点出发),但写法较复杂。(附灵神题解)
1
2
3
4
5
6
7
8
9
10
11
12
13class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
ans = 0
def dfs(node: Optional[TreeNode], depth: int) -> int:
if node is None:
return
depth += 1
nonlocal ans
ans = max(ans, depth)
dfs(node.left, depth)
dfs(node.right, depth)
dfs(root, 0)
return ans - 其实是走了一遍前序遍历,在dfs的过程中逐步累加深度depth,并通过比较更新最大深度ans。(注意nonlocal的使用)
4. 二叉树的最小深度(LC111)
- 与最大深度不同,需要特别考虑左右子树为空的情况:左子树为空时,只取右子树+1;右子树为空时,只取左子树+1.
- 左右都不为空时,取左右子树最小值+1
- 主体思想不变,还是自底向上的后序遍历法
1
2
3
4
5
6
7
8
9class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0
if root.right is None:
return 1 + self.minDepth(root.left)
if root.left is None:
return 1 + self.minDepth(root.right)
return 1 + min(self.minDepth(root.left), self.minDepth(root.right)) - 贴一下灵神的自顶向下方法,明显麻烦太多了。还要考虑一个优化剪枝(当递归中发现cnt ≥ ans,由于继续向下递归也不会让ans变小,直接返回。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
ans = inf
def dfs(node: Optional[TreeNode], cnt: int) -> None:
if node is None:
return
nonlocal ans
cnt += 1
if cnt >= ans:
return # 最优性剪枝
if node.left is node.right: # node 是叶子
ans = cnt
return
dfs(node.left, cnt)
dfs(node.right, cnt)
dfs(root, 0)
return ans if root else 0
5. 翻转二叉树(LC226)
- 可以采用前序和后序,但中序不行
- 后序遍历
1
2
3
4
5
6
7
8
9class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
# 后序遍历
left = self.invertTree(root.left)
right = self.invertTree(root.right)
root.left, root.right = root.right, root.left
return root - 前序遍历
1
2
3
4
5
6
7
8class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
root.left, root.right = root.right, root.left # 交换左右儿子
self.invertTree(root.left) # 翻转左子树
self.invertTree(root.right) # 翻转右子树
return root
6. 二叉树的层序遍历(LC102)
- 前中后序遍历是DFS,层序遍历是BFS
- 队列思想,保存每一层的遍历节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
ans = []
q = deque([root])
while q:
vals = []
for _ in range(len(q)):
node = q.popleft()
vals.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
ans.append(vals)
return ans
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Golden Arc!