第六章 二叉树part01
)
基础知识
二叉树的种类
- 二叉树有两种主要的形式:满二叉树和完全二叉树。
- 满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。也可以说深度为k,有2^k-1个节点的二叉树。
- 完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。
- 二叉搜索树:前面介绍的树,都没有数值的,**而二叉搜索树是有数值的了,二叉搜索树是一个有序树。右边>根>左边。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
- 平衡二叉搜索树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_set底层实现是哈希表。
map、set、multimap,multiset是啥?
二叉树的存储方式
- 二叉树可以链式存储,也可以顺序存储。链式存储方式就用指针, 顺序存储的方式就是用数组。
- 顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。
- 如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树的遍历方式
- 二叉树主要有两种遍历方式, 这两种遍历是图论中最基本的两种遍历方式,后面在介绍图论的时候还会介绍:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。下面的前中后,其实指的就是中间节点的遍历顺序,只要记住前中后序指的就是中间节点的位置就可以了。
- 前序遍历(递归法,迭代法):中左右
- 中序遍历(递归法,迭代法):左中右
- 后序遍历(递归法,迭代法):左右中
- 广度优先遍历:一层一层的去遍历
- 层次遍历(迭代法)
- 深度优先遍历:先往深走,遇到叶子节点再往回走。下面的前中后,其实指的就是中间节点的遍历顺序,只要记住前中后序指的就是中间节点的位置就可以了。
- 做二叉树相关题目,经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历,使用递归是比较方便的。
二叉树的定义
class TreeNode:
def __init__(self, val, left = None, right = None):
self.val = val
self.left = left
self.right = right
递归遍历(必须掌握)
- 递归三要素:
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
题目:前序遍历:
思路:时刻记住递归三要素
- 确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入vector来放节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void
- 确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
- 确定单层递归的逻辑:前序遍历是中左右的顺序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
# 前序遍历
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res=[]
def dfs(node):# 1.确定参数和返回条件
if node is None:#确定终止条件
return
#确定单层递归的逻辑
res.append(node.val)
dfs(node.left)
dfs(node.right)
dfs(root)
return res
# 后序遍历
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
# 中序遍历
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
迭代遍历(基础不好的录友,迭代法可以放过)
统一迭代(基础不好的录友,迭代法可以放过)
层序遍历(广度优先遍历)
思路:层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
使用队列实现二叉树广度优先遍历,如下:
102.二叉树的层序遍历
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
from collections import deque
if not root:
return []
queue=deque([root])# 构建队列
result=[]
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)
result.append(level)
return result
107.二叉树的层次遍历II
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(level)
return res[::-1]
199.二叉树的右视图
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(level[-1])
return res
637.二叉树的层平均值
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
if not root:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(sum(level)/len(level))
return res
429.N叉树的层序遍历
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
for child in cur.children:#遍历每一个子节点
que.append(child)
res.append(level)
return res
515.在每个树行中找最大值
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(max(level))
return res
116.填充每个节点的下一个右侧节点指针
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return root
from collections import deque
que=deque([root])
while que:
prev=None #每层开始时设置prev
for i in range(len(que)):
cur=que.popleft()
if prev:
prev.next=cur
prev=cur
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return root
117.填充每个节点的下一个右侧节点指针II
if not root:
return root
from collections import deque
que=deque([root])
while que:
prev=None #每层开始时设置prev
for i in range(len(que)):
cur=que.popleft()
if prev:
prev.next=cur
prev=cur
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return root
104.二叉树的最大深度(opens new window)
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return []
from collections import deque
que=deque([root])
res=[]
while que:
level=[]
for i in range(len(que)):
cur=que.popleft()
level.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
res.append(level)
return len(res)
111.二叉树的最小深度
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
from collections import deque
que=deque([root])
depth=0
while que:
depth+=1
for i in range(len(que)):
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