理论基础
二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
二叉树的遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
广度优先遍历
- 层次遍历(迭代法)
栈其实是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的。
而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
二叉树的定义
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 = []
self.traversal(root, res)
return res
def traversal(self, cur, res):
if not cur:
return
res.append(cur.val)
self.traversal(cur.left, res)
self.traversal(cur.right, res)
中序遍历
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
self.traversal(root, res)
return res
def traversal(self, cur, res):
if not cur:
return
self.traversal(cur.left, res)
res.append(cur.val)
self.traversal(cur.right, res)
后序遍历
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
self.traversal(root, res)
return res
def traversal(self, cur, res):
if not cur:
return
self.traversal(cur.left, res)
self.traversal(cur.right, res)
res.append(cur.val)
二叉树的迭代遍历
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。 因此我们用栈也可以是实现二叉树的前后中序遍历了。
前序遍历
前序遍历(迭代法)
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 前序遍历,根左右。先遍历根,可以把根写入结果,然后把右节点加入栈,先去遍历左节点
res = []
stack = []
cur = root
while cur:
# 先遍历根节点
res.append(cur.val)
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
if stack:
cur = stack.pop()
else:
break
return res
看题解之后优化的程序
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 前序遍历,根左右。先遍历根,可以把根写入结果,然后把右节点加入栈,先去遍历左节点
res = []
if not root:
return res
stack = [root]
while stack:
node = stack.pop()
res.append(node.val)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return res
中序遍历
前序遍历迭代法,要访问的元素和要处理的元素顺序是一致的,都是中间节点。而中序遍历迭代法,处理顺序和访问顺序是不一致的。
那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。 指针顺着左节点遍历下去,同时使用栈存储路径中的节点,路径结束后将栈中元素拿出来处理,然后处理该元素的右节点
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 中序遍历为左根右
res = []
stack = []
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
后序遍历
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 前序遍历为根左右,后序遍历为左右根。
# 将前序遍历的顺序改为根右左,翻转之后为左右根,即为后序遍历
res = []
if not root:
return res
stack = [root]
while stack:
node = stack.pop()
res.append(node.val)
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
res.reverse()
return res
二叉树的统一迭代遍历
二叉树的统一迭代法 将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。
如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。这种方法也可以叫做标记法
前序遍历
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 前序遍历,根左右。入栈顺序为右左根
res = []
stack = []
if root:
stack.append(root)
while stack:
node = stack.pop()
if node:
if node.right: # 右
stack.append(node.right)
if node.left: # 左
stack.append(node.left)
stack.append(node) # 根
stack.append(None)
else:
node = stack.pop()
res.append(node.val)
return res
中序遍历
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 中序遍历为左根右,那么放入栈的顺序为右根左
res = []
stack = []
if root:
stack.append(root)
while stack:
node = stack.pop()
if node:
if node.right: # 右
stack.append(node.right)
stack.append(node) # 根
stack.append(None)
if node.left: # 左
stack.append(node.left)
else:
node = stack.pop()
res.append(node.val)
return res
后序遍历
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 后序遍历为左右根,入栈顺序为根右左
res = []
stack = []
if root:
stack.append(root)
while stack:
node = stack.pop()
if node:
stack.append(node) # 根
stack.append(None)
if node.right: # 右
stack.append(node.right)
if node.left: # 左
stack.append(node.left)
else:
node = stack.pop()
res.append(node.val)
return res