下面是整理的一些二叉树的常见问题,包括求最大最小深度、前中后序以及层序遍历的递归和非递归解法、由前中序序列构建树等。
1. 求最大深度
1.1 递归解法:非常清晰,递归逻辑即分别求左右子树高度,取大值加一即可
def maxDepth(self, root):
if root is None:
return 0
lefth = self.maxDepth(root.left)
righth = self.maxDepth(root.right)
return max(lefth, righth) + 1
1.2 非递归解法:其实就是层序遍历,用BFS和队列的搭配使用可破。
def maxDepthBFS(self, root):
"""
bfs求最大深度,bfs与队列黄金搭配
:param root:
:return:
"""
if root is None:
return 0
q = [root]
depth = 0
while q:
depth += 1
iter = len(q) # 当前队列中的个数即为该层的节点个数,需要记录下来
for i in range(iter): # 然后一个一个pop出去,同时把子节点压进去
cur_node = q.pop(0)
if cur_node.left is not None:
q.append(cur_node.left)
if cur_node.right is not None:
q.append(cur_node.right)
return depth
2. 求最小深度
递归解法跟最大深度类似,但要注意子树为空时的问题:在求最大深度时,子树为空不妨碍最大深度的值,但是最小深度时,若左子树为空,那么最小深度只能来自于右子树深度,而不是0。
下面代码穷举左右子树是否为空的四种情况,比较清晰
def minDepth(self, root):
if root is None:
return 0
# 两个子树都不空
if root.left is not None and root.right is not None:
return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
# 有一个子树为空,则取大值
elif root.left is None and root.right is None:
return self.minDepth(root.right) + 1
elif root.right is None and root.left is None:
return self.minDepth(root.left) + 1
# 两个子树都为空
else:
return 1
3. 层序遍历
3.1 递归解法:递归函数需要有一个参数level,该参数表示当前结点的层数。遍历的结果返回到一个二维列表res=[[]]
中,res中的每一个子列表保存了对应index层的从左到右的所有结点value值。
def levelOrder_recur(self, root):
res =[[]]
def helper(node, level):
# 递归函数需要有一个参数level,该参数表示当前结点的层数。
if not node:
return
else:
res[level-1].append(node.val) # 加入到对应层中
if len(res) == level:
res.append([]) # 再扩展一行
helper(node.left, level+1)
helper(node.right, level+1)
helper(root, 1)
return res[:-1] # 不算最后一行,因为前一层扩展了一个空行
3.2 非递归解法:求最大深度时已经涉及到,队列中保存同一层的所有节点,通过同时压出和压入来体现同层的关系
def levelOrder(self, root):
"""
:param root:
:return: 层序遍历,并返回层序的列表
"""
if root is None:
return
queue = []
queue.append(root)
result = []
depth = -1
while queue:
depth += 1
result.append([]) # 每多一层就扩展一行
# 记录下当前队列中节点的个数,即为该层的节点个数
iter = len(queue)
for x in range(iter):
# 这个循环是指把同一层的节点全部pop出去,并且同时把下一层的节点push进来
node = queue.pop(0)
result[depth].append(node.val)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
return result
扩展
Q:如果仍然按层遍历,但是每层从右往左遍历怎么办呢?
A:将上面的代码left和right互换即可
Q:如果仍然按层遍历,但是我要第一层从左往右,第二层从右往左,第三从左往右…这种zigzag遍历方式如何实现?
A:将res[level-1].append(node.val)
进行一个层数奇偶的判断,一个用append()
,一个用insert(0,)
4. 前序遍历
4.1 递归解法:中左右的顺序,保存到列表中,注意两个列表的相连是+
号
def preorder_recur(self, root):
if root is None:
return []
return [root.val] + self.preorder(root.left) + self.preorder(root.right)
4.2 非递归解法:用res保存结果,用cur代表当前节点,用stack来存放当前节点的右子节点,然后按照一直往左延伸下去
def preorder(self, root):
stack = []
res = []
cur = root
while stack or cur:
if cur:
res.append(cur.val)
stack.append(cur.right)
cur = cur.left
else:
cur = stack.pop() # 取出保存的右子节点
return res
5. 中序遍历
5.1 递归解法
def inorder_recur(self, root):
if root is None:
return []
return self.inorder_recur(root.left) + [root.val] + self.inorder_recur(root.right)
5.2 非递归:保存当前节点到栈中,从栈取出同时加入结果,并移到右子树
def inorder(self, root):
stack = []
res = []
cur = root
while stack or cur:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
6. 后序遍历
6.1 递归
def postorder_recur(self, root):
if not root:
return []
return self.postorder_recur(root.left) + self.postorder_recur(root.right) + [root.val]
6.2 非递归:左右中的顺序,倒过来是中右左的顺序,跟前序遍历只是左右相反,因此可以把前序遍历的左右交换,最后逆序输出列表,即为答案
def postorder(self,root):
"""
左右中 是中右左的逆序 只要把preorder左右互换即可
:param root:
:return:
"""
stack = []
res = []
cur = root
while stack or cur:
if cur:
res.append(cur.val)
stack.append(cur.left)
cur = cur.right
else:
cur = stack.pop()
return res[::-1]
7. 根据前序、中序序列构建树/求出后序序列
通过前序序列获取根节点,在中序序列中根据根节点划分为左右子树
def getTreePreMid(self, pre, mid):
"""
前序 中序 构建树
:param pre:
:param mid:
:return: root
"""
if len(pre) == 0:
return None
if len(pre) == 1:
return TreeNode(pre[0])
root = pre[0] # 前序序列第一个值就是根节点
root_idx = mid.index(root) # 找到该值在中序序列中的位置
root.left = self.getTreePreMid(pre[1:1+root_idx], mid[0:root_idx]) # 左右两部分分别递归求解
root.right = self.getTreePreMid(pre[1+root_idx:], mid[root_idx+1:])
return root
8. 搜索二叉树路径
下面三种不同的题型都用的是相同的模板,仅供参考
8.1 搜索所有路径:递归函数作用 是从当前节点node往下搜索路径,当前node一定不为空
- 首先将node加入到路径中
- 如果当前节点是叶子节点,路径到尽头,完成一条路径的搜索
- 如果当前节点不是叶子节点,有左子节点,就递归搜索左子树。右子节点也一样
def binaryTreePaths(self, root):
"""
找所有到叶子节点的路径
:param root:
:return: list[str]
"""
if root is None:
return []
res = []
def _backtrace(node, pre_list):
new_list = pre_list
new_list += str(node.val)
if not node.left and not node.right: # 1. 到了叶子节点
res.append(new_list)
return
# 2. 不是叶子节点
if node.left:
_backtrace(node.left, new_list + "->")
if node.right:
_backtrace(node.right, new_list + "->")
_backtrace(root, "")
return res
8.2 求是否存在路径和等于指定值的路径
def hasPathSum(self, root, sum):
if root is None:
return False
res = False
def _backtrace(node, left_sum):
nonlocal res
if res:
return
# 直接开始判断,是叶子节点,且值为left_sum
if not node.left and not node.right and node.val == left_sum:
res = True
return
else:
if node.left:
_backtrace(node.left, left_sum-node.val)
if node.right:
_backtrace(node.right, left_sum-node.val)
_backtrace(root, sum)
return res
8.3 求路径和等于指定值的路径:递归函数的作用是从当前节点node往下搜索路径和等于left_sum的路径,当前node一定不为空。
- 首先将当前节点node加入到路径中
- 如果当前节点是叶子节点,路径到尽头,根据判断条件加入到res中
- 如果当前节点不是叶子节点,有左子节点,就递归搜索左子树。右子节点也一样
def pathSum(self, root, sum):
if not root:
return []
res = []
def _backtrace(node, left_sum, pre_list):
new_list = pre_list.copy() # 注意要拷贝一份,不然是引用变量,后面递归体会改变pre_list
new_list.append(node.val) # 进这个函数的都是node不为空
# 1.叶子节点
if not node.left and not node.right and node.val == left_sum:
res.append(new_list)
else:# 2. 非叶子节点
if node.left: # 左子树不为空
_backtrace(node.left, left_sum-node.val, new_list)
if node.right:
_backtrace(node.right, left_sum-node.val, new_list)
_backtrace(root, sum, [])
return res
8.4 计算从根到叶子节点生成的所有数字之和
def sumNumbers(self, root):
"""
所有路径序列的值 加起来之和
"""
if root is None:
return 0
res = []
sum = 0
def _backtrace(node, pre_list):
new_list = pre_list # 字符串的拷贝直接相等即可,不需要调用copy函数
new_list += str(node.val)
if not node.left and not node.right:
res.append(new_list)
else:
if node.left:
_backtrace(node.left, new_list)
if node.right:
_backtrace(node.right, new_list)
_backtrace(root, "")
for i in res:
sum += int(i)
return sum