文章目录
- 二叉树的遍历
- 前序遍历
- 递归
- 迭代 - 栈
- 中序遍历
- 递归
- 迭代 - 栈
- 莫里斯遍历
- 后序遍历
- 递归
- 迭代 - 栈
- 层序遍历
- BFS-广度优先搜索 - 队列
- 层序遍历-BFS
- N叉树的遍历
- N叉树的前序遍历
- 递归
- 迭代-栈
- N叉树后序遍历
- 递归
- 迭代-栈
- N叉树的层序遍历
- BFS
- 参考链接
二叉树的遍历
前序遍历
左
递归
#class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
def preorderTraverersal(self, root:TreeNode) -> List[int]:
res = []
def dfs(root):
if not root:
return
res.append(root.val)
dfs(root.left)
dfs(root.right)
dfs(root)
return res
迭代 - 栈
- 初始化栈, 并将根节点入栈
- 当栈不为空时,并将根节点入栈
- 弹出栈顶元素
node
,并将值添加到结果 - 如果
node
的右子树非空,将右子树入栈 - 如果
node
的左子树非空,将左子树入栈
图来自参考链接-1
def preorderTraversal(self, root: 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 TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
def dfs(root):
if not root:
return
dfs(root.left)
res.append(root.val)
dfs(root.right)
dfs(root)
return res
迭代 - 栈
图来自参考链接-3
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
if not root:
return res
stack = []
cur = root
while stack or cur:
while cur: # cur入栈,并到达最左端的叶子节点
stack.append(cur)
cur = cur.left
temp = stack.pop()
res.append(temp.val) # 出栈时再加入结果
cur = temp.right
return res
莫里斯遍历
递归
和迭代
的方式都使用了辅助的空间,而莫里斯遍历
的优点是没有使用任何辅助空间。缺点
是改变了整个树的结构,强行把一棵二叉树改成一段链表结构。
图来自参考链接-3
将黄色区域部分挂到节点
5
的右子树上,接着再把2
和5
这两个节点挂到4
节点的右边。这样整棵树基本上就变改成了一个链表了,之后再不断往右遍历
。
图来自参考链接-3
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
cur = root
while cur:
if not cur.left:
# 左子树为空,则打印这个节点,并向右边遍历
res.append(cur.val)
cur = cur.right
else:
# 如果左节点不为空,就将当前节点连带右子树全部挂到左节点的最右子树下面
pre = cur.left
while pre.right and pre.right != cur:
pre = pre.right
if not pre.right:
pre.right = cur
cur = cur.left
else:
pre.right = None
res.append(cur.val)
cur = cur.right
return res
后序遍历
右
递归
#class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
def preorderTraverersal(self, root:TreeNode) -> List[int]:
res = []
def dfs(root):
if not root:
return
dfs(root.left)
dfs(root.right)
res.append(root.val)
dfs(root)
return res
迭代 - 栈
方法 一: 将迭代前序遍历的顺序,逆置过来,从前序
根 左 右变到后序
左 右
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
if not root:
return res
stack = [root]
while stack:
node = stack.pop()
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
res.append(node.val)
return res[::-1]
方法 二:
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
if not root:
return res
cur = root
stack = [root]
while stack:
while cur:
res.append(cur.val)
stack.append(cur)
cur = cur.right
temp = stack.pop()
cur = temp.left
return res[::-1]
方法 三:参考链接 5
后序遍历
每个根节点
都要经过三次
:第一次遇到它时要立即转去处理其左子树,第二次从左子树经由它转去处理右子树,第三次从右子树回来才应该处理根节点数据,然后返回上一层。
执行cur = root
,方便遍历以免引起混淆。cur
的值是当前节点(可能为空),在实现遍历的循环中维持一种不变的关系: ·
- 栈中节点序列的左边是二叉树已经遍历过的部分,右边是尚未遍历的部分;
- 如果
cur
不为空,其父节点就是栈顶节点;cur
为空时栈顶就是应该访问的节点。根据被访问节点是其父节点的的左节点或右节点就可决定下一步该怎么做:若是左节点就转到右节点;若是右节点就应该处理根节点并强制退栈。
函数定义中外层循环内嵌套了一个内层循环,该内层循环的目标是找到下一个应访问的节点。
注意:
- 内层循环找到当前子树的最下最左节点,将其入栈后终止;
- 若被访问节点是其父节点的左子节点,应直接转到其右兄弟节点继续访问;
- 若被处理节点是其父节点的右子节点,设
cur = None
将迫使外层循环的下次迭代弹出并访问更上一层的节点。
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
stack = []
cur = root
while stack or cur:
while cur:
stack.append(cur) # 第一次入栈的是根节点
# 判断当前节点的左子树是否存在,若存在则持续左下行,若不存在就转向右子树
cur = cur.left if cur.left is not None else cur.right
# 循环结束说明走到了叶子节点,没有左右子树了,该叶子节点即为当前栈顶元素,应该访问了
cur = stack.pop() # 取出栈顶元素进行访问
res.append(cur.val) # 将栈顶元素也即当前节点的值添加进res
# (下面的stack[-1]是执行完上面那句取出栈顶元素后的栈顶元素)
if stack and stack[-1].left == cur: #若栈不为空且当前节点是栈顶元素的左节点
cur = stack[-1].right ## 则转向遍历右节点
else:
cur = None # 没有左子树或右子树,强迫退栈
return res
方法 四:标记法,参考链接 1 & 4
每次从栈中弹出元素时,如果
flag
为0
,则需要将flag
变为1
并连同该节点再次入栈,只有当 flag 为1
时才可将该节点加入到结果中。
图来自参考链接-1
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
if not root:
return []
stack = [(0, root)]
while stack:
flag, cur = stack.pop()
if not cur:
continue
if flag == 0:
# 后序
stack.append((1, cur))
stack.append((0, cur.right))
stack.append((0, cur.left))
# 前序
# stack.append((0, cur.right))
# stack.append((0, cur.left))
# stack.append((1, cur))
# 中序
# stack.append((0, cur.right))
# stack.append((1, cur))
# stack.append((0, cur.left))
else: # flag == 1 已遍历过了
res.append(cur.val)
return res
层序遍历
BFS-广度优先搜索 - 队列
广度优先搜索的步骤为:
- 初始化队列
queue
,并将根节点root
加入到队列
中 - 当队列不为空时
- 队列中弹出节点
node
,加入到结果中 - 如果
左子树
非空,左子树加入队列 - 如果
右子树
非空,右子树加入队列
图来自参考链接-1
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
def levelOrder(self, root: TreeNode) -> List[List[int]]:
res = []
if not root:
return []
queue = [root]
while queue:
size = len(queue) # 获取当前队列的长度,此长度相当于这一层节点的个数
level = [] # 存储每一层的节点
for i in range(size):
node = queue.pop(0) # 这里相当于一个队列
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(level)
return res
层序遍历-BFS
图来自参考链接-6
层序遍历 节点进队列和出队列的过程
图来自参考链接-6
N叉树的遍历
N叉树的前序遍历
递归
# class Node:
# def __init__(self, val=None, children=None):
# self.val = val
# self.children = children
def preorder(self, root: 'Node') -> List[int]:
res = []
def dfs(node):
if not node:
return
res.append(node.val)
for child in node.children:
dfs(child)
dfs(root)
return res
迭代-栈
使用一个
栈
来得到前序遍历,需要保证栈顶
的节点就是我们当前遍历到的节点
。首先把根节点入栈,因为根节点是前序遍历中的第一个节点。随后每次我们从栈顶取出一个节点u
,它是我们当前遍历到的节点,并把u
的所有子节点逆序
推入栈中。例如 u 的子节点从左到右为v1, v2, v3
,那么推入栈的顺序应当为v3, v2, v1
,这样就保证了下一个遍历到的节点(即u
的第一个子节点v1
)出现在栈顶的位置。
def preorder(self, root: 'Node') -> List[int]:
res = []
if root is None:
return []
stack = [root]
while stack:
node = stack.pop()
res.append(node.val)
stack.extend(node.children[::-1])
return res
N叉树后序遍历
递归
"""
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
def postorder(self, root: 'Node') -> List[int]:
res = []
def dfs(root):
if not root:
return
node = root.children
for child in node:
dfs(child)
res.append(root.val)
dfs(root)
return res
迭代-栈
先遍历一个节点的所有子节点,再遍历这个节点本身。例如当前的节点为
u
,它的子节点为v1, v2, v3
时,那么后序遍历的结果为[children of v1], v1, [children of v2], v2, [children of v3], v3, u,其中[children of vk]
表示以vk
为根节点的子树的后序遍历结果(不包括vk
本身)。将这个结果反转,可以得到 u, v3, [children of v3]’, v2, [children of v2]’,v1, [children of v1]’,其中[a]'
表示[a]
的反转。此时发现,结果和前序遍历非常类似,只不过前序遍历中对子节点的遍历顺序是v1, v2, v3
,而这里是v3, v2, v1
。
因此可以使用和N叉树
的前序遍历相同的方法,使用一个栈来得到后序遍历。首先把 根节点入栈。当每次从栈顶取出一个节点u
时,就把u
的 所有子节点顺序推入栈中。例如u
的子节点从左到右为v1, v2, v3
,那么推入栈的顺序应当为v1, v2, v3
,这样就保证了下一个遍历到的节点(即u
的第一个子节点v3
)出现在栈顶的位置。在遍历结束之后,把 遍历结果反转,就可以得到后序遍历。
def postorder(self, root: 'Node') -> List[int]:
res = []
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
res.append(node.val)
children = node.children
for child in children:
stack.append(child)
return res[::-1]
N叉树的层序遍历
BFS
"""
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
def levelOrder(self, root: 'Node') -> List[List[int]]:
res = []
if not root:
return []
def bfs(root):
queue = [root]
while queue:
nxt = [] # 下一轮 while提供新数据
temp = [] # 本轮结果
for node in queue:
temp.append(node.val)
for child in node.children:
nxt.append(child)
res.append(temp)
queue = nxt # 赋值新收集到的节点
bfs(root)
return res
简写此过程
def levelOrder(self, root: 'Node') -> List[List[int]]:
res = []
if not root:
return []
queue = [root]
while queue:
res.append([node.val for node in queue])
queue = [child for node in queue for child in node.children if child]
return res
在搜索的同时加上层次信息,根据层次信息判断当前搜索结点的所属子列表。
def levelOrder(self, root: 'Node') -> List[List[int]]:
res = []
if not root:
return []
queue = [(0,root)] #初始化
while queue:
level,node = queue.pop(0)
if len(res) == level: #判断当前结点的层次信息
res.append([node.val])
else:
res[level].append(node.val)
if node.children: #将当前结点的子节点全部加到队列中
for i in node.children:
queue.append((level+1,i))
return res
参考链接
1-参考链接-LeetCode题解-图解二叉树的四种遍历2-参考链接-LeetCode题解3-参考链接-LeetCode题解-动画演示+三种实现4-参考链接-LeetCode题解-全解模板5-参考链接-LeetCode题解-后序遍历6-参考链接-LeetCode题解-BFS使用场景总结7-参考链接-LeetCode题解-N叉树前序遍历官方题解8-参考链接-LeetCode题解-N叉树后序遍历官方题解9-参考链接-LeetCode题解-N叉树层序遍历-DFS+BFS10-参考链接-LeetCode题解-N叉树层序遍历-BFS11-参考链接-LeetCode题解-N叉树层序遍历官方题解