二叉树的基础知识
常见的二叉树
满二叉树: 深度为k,有
2
k
−
1
2^{k}-1
2k−1个结点的二叉树
完全二叉树: 在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 k层,则该层包含
1
−
2
h
−
1
1- 2^{h-1}
1−2h−1个节点。
二叉搜索树: 是一个有序树,左子树的值大于右子树
平衡二叉搜索树: 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树的存储方式
- 链式存储
class TreeNode:
def __init__(self,value):
self.value = value
self.left = None
self.right = None
- 顺序存储
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树的遍历方式
深度优先搜索用栈,广度优先搜索用队列,递归法的搜索方式也必须要掌握。
- 深度优先遍历
- 前序遍历(递归法,迭代法):中左右
- 中序遍历(递归法,迭代法):左中右
- 后序遍历(递归法,迭代法):左右中
- 广度优先遍历
- 层次遍历(迭代法)
递归遍历
递归三部曲(底层是利用栈来实现)
- 确定递归函数的参数和返回值
- 确定终止条件
- 确定单层递归的逻辑
# 伪代码,前中后序的递归搜索方法
def trasersal(cur,vec):
if(cur==NULL):
return
# 中序遍历:中左右
vec.push(cur.val)
trasersal(cur.left,vec)
trasersal(cur.right,vec)
迭代遍历(非递归遍历方式)
前序遍历-中左右
思路:先将右孩子入栈,再将左孩子入栈,这样才能保证先访问左孩子再访问右孩子。注意在写明遍历顺序的过程中,需要注意结点为空的条件。
# 伪代码
stack.append(x)
while stack:
node = stack[-1]
stack.pop()
if node!=NULL:
vec.append(node.val)
else:
continue
# 中左右
stack.append(node.left)
stack.append(node.right)
return vec
后序遍历-左右中
中左右-中右左-左右中(利用到数组的翻转-reverse)
# 伪代码
stack.append(x)
while stack:
node = stack[-1]
stack.pop()
if node!=NULL:
vec.append(node.val)
else:
continue
# 中左右
stack.append(node.right)
stack.append(node.left)
return reverse(vec)
中序遍历-左中右
中序遍历:访问的顺序和遍历的顺序不一样,在前序和后序遍历中访问和处理的结点顺序是一样的。而在中序遍历中访问一个结点,需要处理的是其左右孩子结点。
- 定义一个数组·:记录结果
- 定义一个栈:记录遍历过的元素
- 定义一个指针:记录遍历的结点
- 终止条件:cur指针不为空并且栈不为空,那么循环终止
while cur!= NULL and not stack:
# 需要一个cur指针记录当前访问的结点值是什么。
if cur != NULL:
stack.push(cur)
cur = cur.left
else:
cur = stack[-1] # 第一个需要处理的元素
stack.pop()
cur = cur.right
return result
层序遍历-广度优先搜索(队列)
借助队列保存,每一层遍历过的元素。利用size记录当前层的值。
result = []
queue.append(root)
while queue:
size = lenght(queue)
vec = []
while size:
node = queue[0]
queue.pop(0)
vec.append(node.val)
if node.left != NULL:
queue.append(node.left)
if node.right != NULL:
queue.append(node.right)
size -= 1
return result
leetcode题目练习
144.二叉树的前序遍历-中左右
递归法
# 递归法
def traversal(node,result): #确定函数的参数
if not node: # 确定终止条件
return
result.append(node.val)
traversal(node.left,result) # 确定单次递归的逻辑
traversal(node.right,result)
result = []
traversal(root,result)
return result
遍历法
# 迭代法
stack = []
result = []
stack.append(root)
while stack :
node = stack.pop()
if not node:
continue
else:
result.append(node.val)
stack.append(node.right)
stack.append(node.left)
return result
145.二叉树的后序遍历-左右中
在前顺遍历的基础上,微调顺序并进行反转操作
递归法
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]:
# 递归法
def traversal(node,result): #确定函数的参数
if not node: # 确定终止条件
return
result.append(node.val)
traversal(node.right,result) # 确定单次递归的逻辑
traversal(node.left,result)
result = []
traversal(root,result)
result.reverse()
return result
遍历法
# 迭代法:在中序遍历的基础上进行翻转
stack = []
result = []
stack.append(root)
while stack :
node = stack.pop()
if not node:
continue
else:
result.append(node.val)
stack.append(node.left)
stack.append(node.right)
# 前序遍历:中左右-中右左-左右中
result.reverse()
return result
94.二叉树的中序遍历-左中右
递归算法
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]:
# 递归算法
def traversal(node,result):
if not node:
return
# 递归算法在写一次递归的函数时,不要想的太复杂,看当前函数需要怎么实现即可
traversal(node.left,result)
result.append(node.val)
traversal(node.right,result)
result = []
traversal(root,result)
return result
遍历法
思想简单回顾: 循环的条件变成了结点不为空或者栈不为空,如果这个结点不为空,则遍历左结点;如果这个结点为空,那么可以将栈顶元素弹出,紧接着访问右结点,达到cur指向当前访问元素的作用。
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 遍历法-左中右的思想
cur = root # 记录当前结点
result = []
stack = []
while cur or stack:
if cur :
stack.append(cur)
cur = cur.left
else:
cur = stack.pop() # 第一个需要处理的元素
result.append(cur.val)
cur = cur.right
return result
102.二叉树的层序遍历
广度优先搜索
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
if not root:
return []
queue = []
queue.append(root)
while queue:
tmp = [] # 记录每一层的数值
n = len(queue)
while n:
root = queue[0]
queue.pop(0)
tmp.append(root.val)
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
n -= 1
result.append(tmp)
return result
107.二叉树的层次遍历II
在层序遍历的基础上,进行列表的翻转操作
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
result = []
queue = []
queue.append(root)
while queue:
n = len(queue)
vec = []
while n:
node = queue.pop(0)
vec.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
n -= 1
result.append(vec)
result.reverse()
return result
199.二叉树的右视图
在层序遍历的基础上,记录每一层的最后一个数值
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
result = []
queue = []
queue.append(root)
while queue:
n = len(queue)
vec = []
while n:
node = queue.pop(0)
vec.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
n -= 1
result.append(vec[-1])
return result
637.二叉树的层平均值
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
if not root:
return []
result = []
queue = []
queue.append(root)
while queue:
n = len(queue)
t = n
vec = []
while n:
node = queue.pop(0)
vec.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
n -= 1
result.append(sum(vec)/t)
return result
429.N叉树的层序遍历
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root:
return []
result = []
queue = []
queue.append(root)
while queue:
n = len(queue)
vec = []
while n:
node = queue.pop(0)
vec.append(node.val)
** for child in node.children: **
queue.append(child)
n -= 1
result.append(vec)
return result
515.在每个树行中找最大值
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
result = []
if not root:
return []
queue = []
queue.append(root)
while queue:
tmp = [] # 记录每一层的数值
n = len(queue)
while n:
root = queue[0]
queue.pop(0)
tmp.append(root.val)
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
n -= 1
result.append(max(tmp))
return result
116.填充每个节点的下一个右侧节点指针
记录上一层,连接下一层的结点。
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return root
leftmost = root
while leftmost.left:
head = leftmost
while head:
# 同一个父节点
head.left.next = head.right
if head.next:
head.right.next = head.next.left
head = head.next
leftmost = leftmost.left
return root
117.填充每个节点的下一个右侧节点指针II
对象从完美二叉树,变成了普通的二叉树,在遍历下一层节点的时候,对这一层的节点进行处理。
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return root
queue = [root]
while queue:
n = len(queue)
firstNode = queue[0] #记录第一个结点
while n:
# 在进行层序遍历同时,对next进行处理
node = queue[0]
queue.pop(0)
if firstNode != node:
firstNode.next = node
firstNode = node
if n == 1:
firstNode = None
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
n -= 1
return root
104.二叉树的最大深度
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
result = 0
if not root:
return 0
queue = []
queue.append(root)
while queue:
tmp = [] # 记录每一层的数值
n = len(queue)
while n:
root = queue[0]
queue.pop(0)
tmp.append(root.val)
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
n -= 1
result += 1
return result
111.二叉树的最小深度
判断这个节点是否没有左右子树时对应的result
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
result = 1
if not root:
return 0
queue = []
queue.append(root)
while queue:
tmp = [] # 记录每一层的数值
n = len(queue)
while n:
root = queue[0]
queue.pop(0)
tmp.append(root.val)
if root.left:
queue.append(root.left)
if root.right:
queue.append(root.right)
if not root.left and not root.right:
return result
n -= 1
result += 1
return result