文章讲解:代码随想录 (programmercarl.com)——二叉树理论基础
视频讲解:关于二叉树,你该了解这些!| 二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili
满二叉树:节点数量。
完全二叉树:底部从左到右。
二叉搜索树:左子树所有节点小于中间节点,右子树所有节点大于中间节点,时间复杂度为log(n)。
平衡二叉搜索树:左子树和右子树高度差不能超过1。
链式存储:为一个链表,指针指向两个子节点。
线式存储:用一个字符数组保存二叉树,左子节点为 ,右子节点为 。
递归遍历(深度优先搜索):一直搜到终点再回头换方向,一般用递归实现,也可以用迭代法解决。常见方式:前中后序遍历,前序遍历——中左右,中序遍历——左中右,后续遍历——左右中,什么序中在哪。
层序遍历(广度优先搜索):一层一层或一圈一圈遍历,用迭代法实现。常见方式:层序遍历。
class TreeNode:
def __init__(self, val, left = None, right = None):
self.val = val
self.left = left
self.right = right
题目链接:144. 二叉树的前序遍历 - 力扣(LeetCode)94. 二叉树的中序遍历 - 力扣(LeetCode)145. 二叉树的后序遍历 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——递归遍历
视频讲解:每次写递归都要靠直觉? 这次带你学透二叉树的递归遍历!| LeetCode:144.前序遍历,145.后序遍历,94.中序遍历_哔哩哔哩_bilibili
递归三部曲:
1. 确定递归函数的参数和返回值;
2. 确定终止条件;
3. 确定单层递归的逻辑。
# 前序遍历
# 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]:
res = []
# 确定终止条件
def dfs(node):
if node is None:
return
res.append(node.val) # 中
dfs(node.left) # 左
dfs(node.right) # 右
dfs(root)
return res
# 中序遍历
# 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]:
res = []
# 确定终止条件
def dfs(node):
if node is None:
return
dfs(node.left) # 左
res.append(node.val) # 中
dfs(node.right) # 右
dfs(root)
return res
# 后序遍历
# 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]:
res = []
# 确定终止条件
def dfs(node):
if node is None:
return
dfs(node.left) # 左
dfs(node.right) # 右
res.append(node.val) # 中
dfs(root)
return res
题目链接:144. 二叉树的前序遍历 - 力扣(LeetCode)94. 二叉树的中序遍历 - 力扣(LeetCode)145. 二叉树的后序遍历 - 力扣(LeetCode)
145. 二叉树的后序遍历 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——迭代遍历
视频讲解:写出二叉树的非递归遍历很难么?这次让你不再害怕非递归!|二叉树的非递归遍历 | 二叉树的遍历迭代法 | 前序与中序_哔哩哔哩_bilibili写出二叉树的非递归遍历很难么?这次再带你写出中序遍历的迭代法!|二叉树的非递归遍历 | 二叉树的遍历迭代法_哔哩哔哩_bilibili
思路:用栈实现,前序遍历先放右子节点,再放左子节点。
# 迭代遍历——前序遍历
# 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 []
# 定义一个栈
stack = [root]
res = []
while stack:
node = stack.pop()
# 中节点加入栈
res.append(node.val)
# 右子节点入栈
if node.right:
stack.append(node.right)
# 左子节点入栈
if node.left:
stack.append(node.left)
return res
思路:在前序遍历基础上,左右做一个颠倒,此时为中右左,最后再来一个翻转即可。
# 迭代遍历——后序遍历
# 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 []
# 定义一个栈
stack = [root]
res = []
while stack:
node = stack.pop()
# 中节点加入栈
res.append(node.val)
# 左子节点入栈
if node.left:
stack.append(node.left)
# 右子节点入栈
if node.right:
stack.append(node.right)
return res[::-1]
思路:首先一路向左,加入栈,当遍历到根左子节点的时候,弹出加入数组,弹出中节点,再去找中节点是否有右子节点,不断重复。
Note:用指针遍历节点,用栈记录指针遍历过的元素
# 迭代遍历——中序遍历
# 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 []
# 定义一个栈,记录指针遍历的元素
stack = []
res = []
# 定义一个指针,为根节点
cur = root
# 终止条件:cur为空或栈为空
while cur or stack:
# 如果当前不为空,就将该元素加入栈
if cur:
stack.append(cur)
# 指针向左遍历
cur = cur.left
# 如果遇到空节点,从栈中弹出元素
else:
cur = stack.pop()
# 中节点添加到结果
res.append(cur.val)
# 指针向右遍历
cur = cur.right
return res
文章讲解:代码随想录 (programmercarl.com)——统一迭代
文章讲解:代码随想录 (programmercarl.com)——层序遍历
视频讲解:讲透二叉树的层序遍历 | 广度优先搜索 | LeetCode:102.二叉树的层序遍历_哔哩哔哩_bilibili
思路:借助队列保存每一层遍历过的元素。第一层元素加队列,记录当前层size,弹出size个元素,并加入其左右节点元素,记录第二层size,弹出第一个元素加入其左右子节点元素,size-1,再弹出一个元素,并将其左右子节点加入队列,size-2,直到size=0,记录下一层size,重复弹出记录的操作。
题目链接:102. 二叉树的层序遍历 - 力扣(LeetCode)
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 将当前层元素加入结果
res.append(vator)
return res
题目链接:107. 二叉树的层序遍历 II - 力扣(LeetCode)
Note:相当于给上一道题反转一下。
# 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 levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 将当前层元素加入结果
res.append(vator)
return res[::-1]
题目链接:199. 二叉树的右视图 - 力扣(LeetCode)
Note:取每一行数组最后一个元素
# 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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 将当前层元素加入结果
res.append(vator[-1])
return res
题目链接:637. 二叉树的层平均值 - 力扣(LeetCode)
# 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 numpy as np
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 将当前层元素加入结果
means = np.mean(vator)
res.append(means)
return res
题目链接:429. N 叉树的层序遍历 - 力扣(LeetCode)
Note:改成n个子节点,此时不能用左右,需要遍历
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
for child in cur.children:
que.append(child)
# 将当前层元素加入结果
res.append(vator)
return res
题目链接:515. 在每个树行中找最大值 - 力扣(LeetCode)
# 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 largestValues(self, root: Optional[TreeNode]) -> List[int]:
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个数组放当前层元素
vator = []
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将当前节点值放到当前层的结果中
vator.append(cur.val)
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
# 将当前层元素加入结果
max1 = max(vator)
res.append(max1)
return res
题目链接:116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)
"""
# 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
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
"""
# 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
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义每一层开始之前,还没有前一个节点
prev = None
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 如果不为空,说明不是当前层第一个节点
if prev:
# 将前一个节点的 next 指针指向当前节点,这样就在每一层形成了一个链表
prev.next = cur
# 更新 prev 为当前节点,以便在下一次迭代中使用。
prve = cur
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return root
题目链接:117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)
Note:代码和前一道题一模一样
"""
# 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
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
"""
# 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
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
# 定义一个队列
que = collections.deque()
res = []
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义每一层开始之前,还没有前一个节点
prev = None
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 如果不为空,说明不是当前层第一个节点
if prev:
# 将前一个节点的 next 指针指向当前节点,这样就在每一层形成了一个链表
prev.next = cur
# 更新 prev 为当前节点,以便在下一次迭代中使用。
prve = cur
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return root
题目链接:104. 二叉树的最大深度 - 力扣(LeetCode)
# 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 maxDepth(self, root: Optional[TreeNode]) -> int:
# 定义一个队列
que = collections.deque()
# 初始化深度
depth = 0
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个深度
depth += 1
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return depth
题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)
思路:如果没有左和右子节点,则返回最小深度。
# 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 minDepth(self, root: Optional[TreeNode]) -> int:
# 定义一个队列
que = collections.deque()
# 初始化深度
depth = 0
# 如果根节点不为空则加入队列
if root is not None:
que.append(root)
if not root:
return 0
# 遍历终止条件为队列中没有元素了
while que:
# 记录当前层节点数
size = len(que)
# 定义一个深度
depth += 1
for i in range(size):
# 从队列中弹出当前节点
cur = que.popleft()
# 判断是否有左右子节点
if not cur.left and not cur.right:
return depth
# 将节点左右子节点加入队列
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return depth