本文参考leetcode的数据结构与算法
笔记系列github地址
# 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
三种深度遍历方式的递归与迭代方式都需要掌握
前序遍历的读取顺序为 中左右,即先读取根节点,再读取左子树,最后读取右子树
对于前序遍历的递归实现很简单,仅仅只需要按照中左右的顺序将递归结果拼接起来即可,注意root为空的情况,此时应该输出空值
# 前序遍历递归
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
# 如果不是结点,直接返回空列表
if root:
# 如果是结点,先读根节点
result.append(root.val)
# 再读左子树
if root.left:
result += self.preorderTraversal(root.left)
# 再读右子树
if root.right:
result += self.preorderTraversal(root.right)
return result
对于迭代的实现,需要借助栈(stack)来实现。栈是一种先进后出的结构,具体在python中的实现便是对list的append()操作(在尾部添加)和pop()操作(弹出尾部元素)。
首先维护一个result列表,一个栈,初始元素是根节点。不断弹出栈的尾部元素,将其作为结果输出。然后将这个弹出的元素的右节点和左节点分别放入栈中。为什么这么设置?因为栈是先入后出,所以这样便可以保证下一次循环首先弹出的是左节点(子树),左边读完然后才是右子树,以此实现前序遍历的”中左右“(第一个弹出的就是根节点)
# 前序遍历迭代
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
result = []
stack = [root]
# 当stack不为空
while stack:
# 弹出尾部元素
cur = stack.pop()
# 尾部元素输出
result.append(cur.val)
# 按照先右再左的原则将当前元素的子节点压入栈
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
return result
中序遍历的读取顺序为 左中右,即先读取左子树,再读取根节点,最后读取右子树
同样的道理,对于递归的解法只需要将结果拼接就好
# 中序遍历递归
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
# 递归法
if not root:
return []
return self.inorderTraversal(root.left) + [root.val] +self.inorderTraversal(root.right)
中序遍历的迭代解法跟前序有些不同。中序遍历需要用到指针。
首先指针指向根节点,从根节点到最左边叶结点路径上所有的点填充进栈。
然后弹出栈的尾部元素(首先是最左侧叶结点),然后输出
弹出后指针指向弹出结点的右节点。
# 中序遍历迭代
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
# 迭代法
if not root:
return []
result= []
stack = []
cur = root
# 当stack清空,root也读完后停止
while stack or cur:
# 填充stack直至叶节点
while cur:
stack.append(cur)
cur = cur.left
# stack顶层pop出为输出结果
cur = stack.pop()
result.append(cur.val)
# 进行右子树
cur = cur.right
return result
后序遍历的读取顺序为 左右中,即先读取左子树,再读取右子树,最后读取根节点
# 后序遍历递归
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
后序遍历其实写法上跟前序遍历几乎一样。
同样首先维护一个result列表,一个栈,初始元素是根节点。不断弹出栈的尾部元素,将其作为结果输出。然后将这个弹出的元素的左节点和右节点分别放入栈(跟前序正好相反),此时便可以注意到输出的结果顺序(中右左),最后输出是将结果反向便可以到”左右中“的输出
# 后序遍历迭代
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
result = []
stack = [root]
while stack:
cur = stack.pop()
if cur.left:
stack.append(cur.left)
if cur.right:
stack.append(cur.right)
result.append(cur.val)
return result[::-1]
# 层次遍历
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
result = []
queue = [root]
while queue:
# 构建某一层的暂时result和queue
# 不选择利用append的原因是其复杂度是O(n),若节点过多则很慢
# 而重新构建新queue会更快,因为最多只有一层的结点
temp_res = []
temp_que = []
while queue:
# 针对每一个pop出的结点,将值计入result中,并且将其结点推入queue中
temp = queue.pop(0)
temp_res.append(temp.val)
if temp.left:
temp_que.append(temp.left)
if temp.right:
temp_que.append(temp.right)
queue = temp_que
result.append(temp_res)
return result
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
elif not root.left and not root.right:
return 1
else:
leftdepth = self.maxDepth(root.left)
rightdepth = self.maxDepth(root.right)
if leftdepth > rightdepth:
return leftdepth+1
else:
return rightdepth+1
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
result = maxDepth(root)
return result != -1
# 改进版maxdeopth, 如果平衡,则返回深度,不平衡返回-1
def maxDepth(root):
if not root:
return 0
else:
ldepth = maxDepth(root.left)
if ldepth == -1:
return -1
rdepth = maxDepth(root.right)
if rdepth == -1:
return -1
c = ldepth - rdepth
if abs(c) <= 1:
return max(ldepth, rdepth) + 1
else:
# -1 代表不平衡
return -1
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if root:
return isMirror(root.left, root.right)
else:
return True
def isMirror(root1, root2):
if not root1 and not root2:
return True
if not root1 or not root2:
return False
if root1.val != root2.val:
return False
return isMirror(root1.left, root2.right) and isMirror(root1.right, root2.left)
# 递归
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if not root:
return False
if not root.left and not root.right:
return root.val == sum
return self.hasPathSum(root.left, sum-root.val) or self.hasPathSum(root.right, sum-root.val)
# 非递归 (利用队列实现)
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if not root:
return False
que_node = [root]
que_val = [root.val]
while que_node and que_val:
# 弹出 node 和 val
cur_node = que_node.pop(0)
cur_val = que_val.pop(0)
# 如果node是叶节点,返回结果
if isLeaf(cur_node) and cur_val == sum:
return True
# 添加子节点
if cur_node.left:
que_node.append(cur_node.left)
que_val.append(cur_val + cur_node.left.val)
if cur_node.right:
que_node.append(cur_node.right)
que_val.append(cur_val + cur_node.right.val)
return False
def isLeaf(root):
return root and not root.left and not root.right
# 初始版本
# 利用了python的一些特性,但效果不是很好
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
if not inorder:
return
if len(inorder) == 1:
return TreeNode(inorder[0])
# 根据后序得到根节点的值和根节点在中序中的位置
root_val = postorder[-1]
in_root_index = inorder.index(root_val)
# 以根节点为分隔,得到左子树和右子树的中序遍历和后序遍历
in_left = inorder[:in_root_index]
in_right = inorder[in_root_index+1:]
l1 = len(in_left)
l2 = len(in_right)
post_left = postorder[:l1]
post_right = postorder[l1: l1+l2]
# 递归得到结果
root = TreeNode(root_val)
root.left = self.buildTree(in_left, post_left)
root.right = self.buildTree(in_right, post_right)
return root
# 同样的思路,效果更好,注意看注释
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
def travesal(in_left, in_right):
# in_left, in_right表示当前inorder在原始inorder上面的区间索引
# 初始入口为 travesal(0, n-1)
# 停止条件1 None
if in_left > in_right:
return None
root = TreeNode(postorder.pop()) # 此时后序弹出最后一个元素为根节点
index_root = indexmap[root.val] # 找到根节点在中序中索引作为切割点
# 以根节点索引为切割点,左边是左子树的中序遍历, 右边是右子树的中序遍历,进行递归
# 注意这里一定要先构建右子树,因为每递归一次,后序就会弹出最后一个元素
# 根据后序的左右中顺序,这个弹出的元素总会是当前根节点的右子树的根节点
root.right = travesal(index_root+1, in_right)
root.left = travesal(in_left, index_root-1)
return root
# 构建中序遍历的索引值哈希表,这一步是为了减少每次查询根节点位置时候的时间
indexmap = {value:index for index, value in enumerate(inorder)}
return travesal(0, len(inorder)-1)
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
def travesal(in_left, in_right):
# in_left, in_right表示当前inorder在原始inorder上面的区间索引
# 初始入口为 travesal(0, n-1)
# 停止条件1 None
if in_left > in_right:
return None
root = TreeNode(postorder.pop()) # 此时后序弹出最后一个元素为根节点
index_root = indexmap[root.val] # 找到根节点在中序中索引作为切割点
# 以根节点索引为切割点,左边是左子树的中序遍历, 右边是右子树的中序遍历,进行递归
# 注意这里一定要先构建右子树,因为每递归一次,后序就会弹出最后一个元素
# 根据后序的左右中顺序,这个弹出的元素总会是当前根节点的右子树的根节点
root.right = travesal(index_root+1, in_right)
root.left = travesal(in_left, index_root-1)
return root
# 构建中序遍历的索引值哈希表,这一步是为了减少每次查询根节点位置时候的时间
indexmap = {value:index for index, value in enumerate(inorder)}
return travesal(0, len(inorder)-1)