最近实验室的事情有点多,天天忙着跑图,我没招了,尽量找时间写
今天开了一个新的,二叉树
二叉树理论基础
定义
二叉树是一种树形数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以为空(不包含任何节点),或由一个根节点和两棵互不相交的子树(左子树和右子树)构成,子树本身也是二叉树。
种类
满二叉树
所有非叶子节点都有两个子节点,且所有叶子节点位于同一层级。若树的高度为 ,则节点总数为
。
完全二叉树
除最后一层外,其他层节点数达到最大值,且最后一层节点从左到右连续排列。常用于堆的实现。
二叉搜索树(BST)
左子树所有节点的值均小于根节点,右子树所有节点的值均大于根节点。中序遍历结果是有序序列。
平衡二叉树
任意节点的左右子树高度差不超过 1 ,例如 AVL 树或红黑树,保证操作效率为 。
退化二叉树
每个节点只有一个子节点,退化为链表结构,操作效率降至 。
存储方式
链式存储
通过节点对象动态分配内存,每个节点包含数据域和左右指针域。
python
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
顺序存储(数组)
按层级顺序将节点存储在数组中,下标为 的节点的左子节点下标为
,右子节点为
。适合完全二叉树,节省指针空间。
递归遍历
深度优先遍历(DFS)
-
前序遍历:根 → 左 → 右
-
中序遍历:左 → 根 → 右(二叉搜索树中结果为有序序列)
-
后序遍历:左 → 右 → 根
-
有一个记忆小妙招就是:根在前,就是前序遍历;根在中,就是中序遍历;根在后,就是后续遍历。剩下两个位置就是先左后右
代码实例(前序遍历)
python
def preorder(root):
if not root:
return []
return [root.val] + preorder(root.left) + preorder(root.right)
这个时候可以刷力扣上面的题
- 144.二叉树的前序遍历(opens new window)
https://leetcode.cn/problems/binary-tree-preorder-traversal/ - 145.二叉树的后序遍历(opens new window)
https://leetcode.cn/problems/binary-tree-postorder-traversal/ - 94.二叉树的中序遍历
https://leetcode.cn/problems/binary-tree-inorder-traversal/
提交

前序遍历
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return [root.val]+self.preorderTraversal(root.left)+self.preorderTraversal(root.right)
后序遍历
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return self.postorderTraversal(root.left)+self.postorderTraversal(root.right)+[root.val]
中序遍历
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return self.inorderTraversal(root.left)+[root.val]+self.inorderTraversal(root.right)
这里需要注意的就是:
1.返回的是列表[],包括后面root.val都是加了方括号
2.前面self.preorderTraversal,就是需要加一个self.
非递归实现(使用栈)
python
def preorder_stack(root):
stack, res = [root], []
while stack:
node = stack.pop()
if node:
res.append(node.val)
stack.append(node.right) # 右子节点先入栈
stack.append(node.left)
return res
感觉递归就是有点难理解具体的过程,但是代码比较简洁
非递归相对来说可读性高一点,但是代码写的多一点。
但是我之前写遇到比较多的还是递归的这种写法
层序遍历
广度优先遍历(BFS)
按层级从上到下、从左到右遍历,通常使用队列实现。
思路
-
先判断树空不空,空就直接返回空。
-
用一个队列,从根节点开始,一层一层 "排队" 遍历。
-
关键:每次先记住当前队列有多少个节点 = 这一层有多少个节点。
-
循环这么多次,把这一层所有节点取出来,存到结果里。
-
每取出一个节点,就把它的左孩子、右孩子重新放进队列(下一层)。
-
一层走完,把这一层结果存起来,继续下一层,直到队列为空。
下面是一个比较经典的层序遍历的实现方式
python
# 利用长度法实现二叉树的【层序遍历】(广度优先 BFS)
# 作用:按一层一层的顺序,把二叉树节点的值输出为二维列表
# 每一层对应结果里的一个子列表
# 二叉树节点定义
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val # 节点值
# self.left = left # 左子节点
# self.right = right # 右子节点
import collections
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
# 边界判断:如果根节点为空,直接返回空列表
if not root:
return []
# 初始化队列:把根节点放进去,队列用于 BFS 按层遍历
# collections.deque 是双端队列,popleft() 效率更高
queue = collections.deque([root])
# 最终结果列表:存储每一层的节点值(二维列表)
result = []
# 只要队列不为空,就说明还有节点没遍历完
while queue:
# 临时列表:存储【当前这一层】的所有节点值
level = []
# 核心:先记录当前队列的长度 = 当前层的节点个数
# 固定长度,保证 for 循环只遍历【本层】的节点,不会混入下一层
for _ in range(len(queue)):
# 从队列头部取出一个节点(先入先出)
cur = queue.popleft()
# 把当前节点的值加入本层列表
level.append(cur.val)
# 如果当前节点有左孩子,加入队列(下一层)
if cur.left:
queue.append(cur.left)
# 如果当前节点有右孩子,加入队列(下一层)
if cur.right:
queue.append(cur.right)
# 本层遍历完毕,把本层结果加入最终总结果
result.append(level)
return result
据说学完了层序遍历,可以一下打10个题
- 102.二叉树的层序遍历(opens new window)
- 107.二叉树的层次遍历II(opens new window)
- 199.二叉树的右视图(opens new window)
- 637.二叉树的层平均值(opens new window)
- 429.N叉树的层序遍历(opens new window)
- 515.在每个树行中找最大值(opens new window)
- 116.填充每个节点的下一个右侧节点指针(opens new window)
- 117.填充每个节点的下一个右侧节点指针II(opens new window)
- 104.二叉树的最大深度(opens new window)
- 111.二叉树的最小深度
提交
**102.**二叉树的层序遍历
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(level)
return res
在 queue=collections.deque([root])不要忘记了root
**107.**二叉树的层次遍历 II
107. 二叉树的层序遍历 II - 力扣(LeetCode)
思路:我能不能直接先从上到下遍历,然后在返回结果的时候把列表的顺序颠倒一下
好的,提交通过,在上面基础上改了一行代码
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(level)
return res[::-1]
**199.**二叉树的右视图
思路:也就是说,我需要这一层的最右边那个元素,然后把每层最右边的放到一起
提交通过,也是只改了一行代码 res.append(level[-1])
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(level[-1])
return res
637.二叉树的层平均值
思路:平均值,那我要统计每层的数量,每层的总和,要用总和除以每层的数量。
看起来简单,其实需要注意的地方不少
1.我刚开始直接在后面加res.append(sum1/len(queue)),但是这个时候在for循环之后,len(queue)已经不是本层的长度,而是存储的下一层的数据。所以我们需要再for循环之前用一个变量获取到本层的长度
2.sum1=0的位置,因为是每层的平均值,每层都需要初始化,所以是在while循环里面
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
sum1=0
count=len(queue)
for _ in range(len(queue)):
cur=queue.popleft()
sum1+=cur.val
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(sum1/count)
return res
429.N叉树的层序遍历
题目链接429. N 叉树的层序遍历 - 力扣(LeetCode)
思路:就是这里写下一层queue不能直接判断左右孩子,而是判断他有没有孩子,先试一下
但是要怎么遍历他的每一个孩子?
原来这里是,判断有无孩子,然后extend直接把这个节点的孩子全部加进来
python
"""
# Definition for a Node.
class Node:
def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
self.val = val
self.children = children
"""
import collections
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
# 若当前节点存在子节点
if cur.children:
# 将所有子节点加入队列,作为下一层待遍历节点
queue.extend(cur.children)
res.append(level)
return res
515.在每个树行中找最大值
题目链接515. 在每个树行中找最大值 - 力扣(LeetCode)
思路:把每层元素存到一个数组之后,找出这个数组的最大值,加入结果数组
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
queue=collections.deque([root])
res=[]
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
res.append(max(level))
return res
116.填充每个节点的下一个右侧节点指针
116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)
直接加#,结果他把后面的括号给注释掉了。我刚开始完全没怎么看题,只看输出,就在那写。
不是直接在数组每行后面加#,是要对每行的next做出修改
python
"""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
import collections
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return root
q = collections.deque([root])
while q:
level_size = len(q)
for i in range(level_size):
node = q.popleft()
# 不是本层最后一个,next 指向右边节点
if i < level_size - 1:
node.next = q[0]
# 加入孩子
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return root
q[0]的理解
假设这一层有 3 个节点:A → B → C队列一开始是:[A, B, C]
第一步:处理 A
- 执行
node = q.popleft() - 队列变成:
[B, C] - 此时 A 的右边就是 B
- 所以
A.next = q[0]→ 就是B
117.填充每个节点的下一个右侧节点指针II
题目链接117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)
上道题一样的代码,提交也能通过。
104.二叉树的最大深度
题目链接104. 二叉树的最大深度 - 力扣(LeetCode)
思路:题目描述就是数最多有几层,我们平时不就是一层一层遍历的,那while循环就是每一层,那我让count在while循环里面数就行了。
注意一下返回值是整数,所以之前判断根为空的时候应该返回0,而不是空数组。
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
queue=collections.deque([root])
count=0
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
count+=1
return count
111.二叉树的最小深度
题目链接111. 二叉树的最小深度 - 力扣(LeetCode)
思路:如果一个节点,既没有左孩子又没有右孩子,那就可以在while中停止计数。
其实我刚开始想用break停止计数,但是break只跳出了一层循环,但是我要的是,遇到没有孩子的直接返回结果,这里是在循环里面直接return count
至于count的初始值为什么是1,我刚开始给的0,然后结果运行都少了1,我就直接加了1
我去搜了一下:最小深度从根节点开始算,根节点就是第 1 层,所以 count 初始 = 1。
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
import collections
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
queue=collections.deque([root])
count=1
while queue:
level=[]
for _ in range(len(queue)):
cur=queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
if not cur.right and not cur.left:
return count
count+=1
终于写完这一小节的题了,没有力气,也没有手段