文章目录

  • 二叉树的遍历
  • 前序遍历
  • 递归
  • 迭代 - 栈
  • 中序遍历
  • 递归
  • 迭代 - 栈
  • 莫里斯遍历
  • 后序遍历
  • 递归
  • 迭代 - 栈
  • 层序遍历
  • BFS-广度优先搜索 - 队列
  • 层序遍历-BFS
  • N叉树的遍历
  • N叉树的前序遍历
  • 递归
  • 迭代-栈
  • N叉树后序遍历
  • 递归
  • 迭代-栈
  • N叉树的层序遍历
  • BFS
  • 参考链接


二叉树的遍历

python树形图 python树状图遍历_python树形图

前序遍历

python树形图 python树状图遍历_数据结构_02python树形图 python树状图遍历_数据结构_02

递归

#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

中序遍历

python树形图 python树状图遍历_数据结构_02python树形图 python树状图遍历_数据结构_02

递归

# 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

迭代 - 栈

python树形图 python树状图遍历_二叉树_06


图来自参考链接-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

莫里斯遍历

递归迭代的方式都使用了辅助的空间,而莫里斯遍历的优点是没有使用任何辅助空间。
缺点是改变了整个树的结构,强行把一棵二叉树改成一段链表结构。

python树形图 python树状图遍历_python树形图_07


图来自参考链接-3

将黄色区域部分挂到节点5的右子树上,接着再把25这两个节点挂到4节点的右边。这样整棵树基本上就变改成了一个链表了,之后再不断往右遍历

python树形图 python树状图遍历_List_08


图来自参考链接-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

后序遍历

python树形图 python树状图遍历_数据结构_02python树形图 python树状图遍历_数据结构_02

递归

#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

迭代 - 栈

方法 一: 将迭代前序遍历的顺序,逆置过来,从前序python树形图 python树状图遍历_二叉树_11python树形图 python树状图遍历_二叉树_11变到后序python树形图 python树状图遍历_二叉树_11python树形图 python树状图遍历_二叉树_11

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

每次从栈中弹出元素时,如果 flag0,则需要将flag变为1 并连同该节点再次入栈,只有当 flag 为1时才可将该节点加入到结果中。

python树形图 python树状图遍历_List_15


图来自参考链接-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,加入到结果中
  • 如果左子树非空,左子树加入队列
  • 如果右子树非空,右子树加入队列

python树形图 python树状图遍历_python树形图_16


图来自参考链接-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

python树形图 python树状图遍历_python树形图_17


图来自参考链接-6 层序遍历 节点进队列和出队列的过程

python树形图 python树状图遍历_数据结构_18


图来自参考链接-6

N叉树的遍历

python树形图 python树状图遍历_python树形图_19

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叉树层序遍历官方题解