写在前边的话
今天开启了二叉树的训练,对于二叉树是不太熟悉了,注定今天是任重道远的一天,加油加油!
ps:由于这部分知识学的不好,所以打算按照先看视频然后写代码的形式学了。
二叉树的理论基础
种类
- 满二叉树
- 完全二叉树
- 二叉搜索树
- 平衡二叉搜索树
存储方式
- 链式存储
- 线性存储(下标i的左右孩子分别为2*i+1, 2*i+2)
遍历方式
1. 深度优先遍历
一般迭代法借助栈实现。
- 前序遍历(递归、迭代)
- 中序遍历(递归、迭代)
- 后续遍历(递归、迭代)
2. 广度优先遍历
一般使用队列实现。
- 层序遍历(迭代法)
定义方式
python
class TreeNode():
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
java
public class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode(){};
TreeNode(int val){
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right){
this.val = val;
this.left = left;
this.right = right;
}
}
递归遍历
文章讲解
视频讲解
递归算法三要素
1. 确定递归函数的参数和返回值
2. 确定终止条件
3. 确定单层递归的逻辑
在遍历二叉树的时候,递归函数的参数一般都是一个根结点,一个数组(用于存储遍历的结果),一般没有返回值;终止条件是遇到空节点;递归逻辑写在下边三种遍历中。
前序遍历
题目链接
递归逻辑
先将中节点保存到数组中,然后遍历左子树,最后遍历右子树。
代码编写
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]:
res = []
def dsf(root):
if not root:
return
res.append(root.val)
dsf(root.left)
dsf(root.right)
dsf(root)
return res
java
中序遍历
题目链接
递归逻辑
先将中节点保存到数组中,然后遍历左子树,最后遍历右子树。
代码编写
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]:
res = []
def dsf(root):
if not root:
return
dsf(root.left)
res.append(root.val)
dsf(root.right)
dsf(root)
return res
java
后序遍历
题目链接
递归逻辑
先将中节点保存到数组中,然后遍历左子树,最后遍历右子树。
代码编写
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]:
res = []
def dsf(root):
if not root:
return
dsf(root.left)
dsf(root.right)
res.append(root.val)
dsf(root)
return res
java
迭代遍历
文章讲解
视频讲解
代码编写
python
前序遍历
思路:借助栈,1. 初始化栈为根结点 2. 循环出栈处理节点,处理的是中节点,将val存储到返回列表中,然后依次判断右左子孩子是否为空,不为空则依次右左入栈,这样保证出栈存入列表中的顺序是中左右。
# 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:
tmp = stack.pop()
res.append(tmp.val)
if tmp.right:
stack.append(tmp.right)
if tmp.left:
stack.append(tmp.left)
return res
后序遍历
思路:同前序遍历,不同的是,入栈的顺序是左右,这样存入列表中的数据是中右左,再翻转一下数组(可以使用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 []
stack = [root]
res = []
while stack:
tmp = stack.pop()
res.append(tmp.val)
if tmp.left:
stack.append(tmp.left)
if tmp.right:
stack.append(tmp.right)
return res[::-1]
中序遍历
思路:一路向左,判断左孩子是否为空,不为空存储到栈中,直到左孩子为空,然后出栈获取val存入列表中,然后判断当前节点的右孩子是否为空不为空则入栈,然后重复上述操作。
# 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
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
java
统一迭代 ---暂时放过啦
层序遍历
文章讲解
视频讲解
题目链接
代码编写
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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
res = []
cur = [root]
while cur:
tmp1 = []
tmp2 = []
for i in cur:
tmp1.append(i.val)
if i.left:
tmp2.append(i.left)
if i.right:
tmp2.append(i.right)
cur = tmp2
res.append(tmp1)
return res
用队列实现--deque
# 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]]:
if not root:
return []
res = []
queue = collections.deque([root])
while queue:
tmp = []
n = len(queue)
for i in range(n):
node = queue.popleft()
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(tmp)
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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
res = []
queue = [root]
while queue:
tmp = []
n = len(queue)
for i in range(len(queue)):
node = queue.pop(0) # 模拟队列需要pop(0)
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(tmp)
return res
总结:上边的实现方式,时间复杂度都是O(n),空间复杂度都是O(w)(w是二叉树的最大宽度),但是由于deque有比较高效的内存管理机制,在实际应用中可能会有更好的性能表现。
java
层序遍历拓展题目
107.二叉树的层序遍历||
题目链接
题目难度
中等
代码编写
--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 levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
res = []
queue = collections.deque([root])
while queue:
tmp = []
n = len(queue)
for i in range(n):
node = queue.popleft()
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(tmp)
return res[::-1]
--java
199. 二叉树的右视图
题目链接
题目难度
中等
代码编写
--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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
res = []
cur = [root]
while cur:
tmp = []
res.append(cur[-1].val)
for node in cur:
if node.left:
tmp.append(node.left)
if node.right:
tmp.append(node.right)
cur = tmp
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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
queue = collections.deque([root])
res = []
while queue:
n = len(queue)
for i in range(n):
node = queue.popleft()
if i == n-1: # 遍历到最后一个数据保存到返回列表中
res.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
总结:上边的两种实现方式,时间复杂度都是O(n),空间复杂度都是O(w)(w是二叉树的最大宽度),但是由于deque有比较高效的内存管理机制,在实际应用中可能会有更好的性能表现。
--java
637.二叉树的层平均值
题目链接
题目难度
简单
代码编写
--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 averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
if not root:
return []
res = []
queue = collections.deque([root])
while queue:
n = len(queue)
node_sum = 0
for i in range(n):
node = queue.popleft()
node_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(node_sum/n)
return res
--java
429.N叉树的层序遍历
题目链接
题目难度
中等
代码编写
--python
"""
# 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]]:
if not root:
return []
res = []
queue = collections.deque([root])
while queue:
n = len(queue)
this_level = []
for i in range(n):
node = queue.popleft()
this_level.append(node.val)
if node.children:
queue.extend(node.children) # 也可以使用循环添加
res.append(this_level)
return res
--java
515.在每个树行中找最大值
题目链接
题目难度
中等
代码编写
--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 largestValues(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
res = []
queue = collections.deque([root])
while queue:
n = len(queue)
max_num = -float('inf')
for i in range(n):
node = queue.popleft()
if node.val > max_num:
max_num = node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(max_num)
return res
--java
116.填充每个节点的下一个右侧节点指针
题目链接
题目难度
中等
代码编写
--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
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return root
queue = collections.deque([root])
while queue:
n = len(queue)
if n == 1: # 如果本层只有一个节点则直接指向None,然后还需要判断下一层
node = queue.popleft()
node.next = None
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
else: # 如果本层大于一个节点,则先将第一个节点指定为pre节点,然后遍历本层节点
pre_node = queue.popleft()
for i in range(1, n):
node = queue.popleft()
pre_node.next = node
if i == n-1:
node.next = None
if pre_node.left:
queue.append(pre_node.left)
if pre_node.right:
queue.append(pre_node.right)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
pre_node = node
return root
看了卡哥的代码,觉得更加简洁,在遍历每层节点的时候前置设置一个空节点。
"""
# 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]':
if not root:
return root
queue = collections.deque([root])
while queue:
n = len(queue)
pre_node = None
for i in range(n):
node = queue.popleft()
if pre_node:
pre_node.next = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
pre_node = node
return root
--java
117.填充每个节点的下一个右侧节点指针II
题目链接
题目难度
中等
代码编写
--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
"""
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return root
queue = collections.deque([root])
while queue:
n = len(queue)
pre_node = None
for i in range(n):
node = queue.popleft()
if pre_node:
pre_node.next = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
pre_node = node
return root
--java
104.二叉树的最大深度
题目链接
题目难度
简单
代码编写
--python
依然是层序遍历,初始化一个返回值0,然后每遍历一层就加1。
# 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:
if not root:
return 0
res = 0
queue = collections.deque([root])
while queue:
res += 1
n = len(queue)
for i in range(n):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
--java
111.二叉树的最小深度
题目链接
题目难度
简单
代码编写
--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 minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
res = 0
queue = collections.deque([root])
while queue:
n = len(queue)
res += 1
flag = 0
for i in range(n):
node = queue.popleft()
if node.left == None and node.right == None:
flag = 1
break
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if flag:
break
return res
我是设置了个标志位来查看是否当前层存在左右孩子均为空的节点,看了卡哥的思路是直接return结果,感觉更加简洁。
# 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:
if not root:
return 0
res = 0
queue = collections.deque([root])
while queue:
n = len(queue)
res += 1
for i in range(n):
node = queue.popleft()
if node.left == None and node.right == None:
return res
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
--java
今日总结
今日主要学习了通过递归法、迭代法来获取二叉树的前中后序遍历数据以及层序遍历法来获取二叉树数据,总的来说相比之前熟悉了很多,尤其是层序遍历后边十道题的训练,看到层序遍历的题目能够很快的反应过来。由于二叉树不太熟因此只看了python的,而且统一迭代法也暂时放过了,java的还没来得及看,后边有时间再补上吧。