代码随想录算法训练营第十二天 | 二叉树的递归遍历、迭代遍历、统一遍历和层序遍历

二叉树的遍历:

二叉树的遍历分为深度优先遍历(向下遍历到叶子再返回)和广度优先遍历(先遍历当前层再遍历下一层)
而深度优先遍历 dfs 根据读取的根节点的位置可以分为:

  • 前序遍历(根左右)
  • 中序遍历(左根右)
  • 后序遍历(左右根)

二叉树的递归遍历:

完成递归算法分三步:(PS:被建议的是企业中不太建议用递归,边界条件和debug比较麻烦)

  • 确定函数传入的参数和返回值
  • 确定边界条件
  • 确定正常递归下去的条件
    以二叉树的前序遍历为例(python)
# Definition of TreeNode
class TreeNode(object):
	def __init__(self, val=0, left=None, right=None):
		self.val = val
		self.left = left
		self.right = right
# 前序遍历
class Solution(object):
	def preorderTraversal(self, root):
		result = []
		# 遍历二叉树,传入参数应当为二叉树的节点,节点的值直接记录在result中,因此不需要返回值
		def pre_dfs(tree_node):
			# 确定边界条件为tree_node == None
			if not tree_node:
				return 
			# 正常递归下去的条件
			result.append(tree_node.val)
			pre_dfs(tree_node.left)
			pre_dfs(tree_node.right)
		pre_dfs(root)
		return result

对应的题目:

144.二叉树的前序遍历
145.二叉树的后序遍历
145.二叉树的中序遍历


二叉树的迭代遍历

使用栈完成前中后序遍历,访问二叉树结点的顺序一定是先根后孩子,那么前中后序遍历可以是结点的出栈顺序

  • 前序遍历
    前序遍历根左右与结点的访问顺序相同,因此是根入栈后出栈,并将其孩子结点入栈(先右后左,因为需要出栈顺序为根左右)
class TreeNode(object):
	def __init__(self, val=0, left=None, right=Node)
		self.val = val
		self.left = left
		self.right = right
class Solution(object):
	def preorderTraversal(self, root):
		if not root:
			return []
		result, stack = [], [root]
		while stack:
			node = stack.pop()
			result.append(node.val)
			if node.right: stack.append(node.right)
			if node.left: stack.append(node.left)
		return result
  • 中序遍历
    中序遍历左根右与结点访问顺序不同,因此需要使用指针指向当前访问的结点,同时出栈顺序仍然是遍历的结点。一直向左遍历当前结点,为空时出栈并访问栈顶元素,此时已经遍历了左根,再让指针指向当前结点的右孩子
class Solution(object):
	def inorderTraversal(self, root):
		if not root:
			return []
		stack, result = [root], []
		cur = root
		while cur or stack:
			if cur:
				stack.append(cur)
				cur = cur.left
			else:
				cur = stack.pop()
				# 当前已经访问了左根,接着访问右子树
				result.append(cur.val)
				cur = cur.right 
  • 后序遍历
    后序遍历左右根与结点访问顺序不同,但是可以后序遍历可以由前序遍历的代码更改得到,只需要将出栈顺序改为根右左,最后将结果反转即可
class TreeNode(object):
	def __init__(self, val=0, left=None, right=None):
		self.val = val
		self.left = left
		self.right = right
class Solution(object):
    def postorderTraversal(self, root):
        result = []
        if not root:
            return result
        stack = [root]
        while stack:
            node = stack.pop()
            result.append(node.val)
            # 需要出栈顺序为中右左
            if node.left: stack.append(node.left)
            if node.right: stack.append(node.right)
        # 最后对result进行反转
        result = result[::-1]
        return result

二叉树的统一迭代遍历:

统一二叉树的前中后序遍历的代码(也即对前序代码进行修改可得到中序遍历的代码),代码不统一的问题出现在访问结点与遍历结点不同,那么我们在入栈结点时,对遍历的结点进行标记,从而出栈根据标记进行遍历。此处的标记为在遍历的结点进栈后再进栈None。同时不同遍历顺序的结点进栈顺序不同

# 中序
class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        result = []
        stack = []
        if root : stack.append(root)
        while stack:
            node = stack.pop()
            # node不为None,表明需要入栈结点
            if node:
                # 先入栈右结点
                if node.right: stack.append(node.right)
                # 再入栈根节点和None表明是node需要遍历的结点
                stack.append(node)
                stack.append(None)
                # 最后入栈左结点
                if node.left: stack.append(node.left)
            # node为None,表明接下来是需要遍历的结点
            else:
                node = stack.pop()
                result.append(node.val)
        return result



# 前序
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        result = []
        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()
                result.append(node.val)
        return result



# 后序
class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root:
            return []
        result = []
        stack = [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()
                result.append(node.val)
        return result
#

二叉树的层序遍历:

使用队列
对于下面这个二叉树,层序遍历返回的为[[5], [6,4], [10, 8, 12]] (逐层从左到右访问所有结点)
在这里插入图片描述

# 迭代
from collections import deque
class TreeNode(object):
	def __init__(self, val=0, left=None, right=None)
		self.val = val
		self.left = left
		self.right = right
def levelOrder(root):
	result = []
	queue = deque()
	if root: queue.append(root)
	while queue:
		q_size = len(queue)
		level_r = []
		for i in range(q_size):
			cur = queue.popleft()
			level_r.append(cur.val)
			if cur.left: queue.append(cur.left)
			if cur.right: queue.append(cur.right)
		result.append(level_r)
	return result


# 递归实现
def levelOrder(root):
	levels = []
	# node是当前访问的结点,level用于标记node的层
	def bfs_dfs(node, level)
		# 边界条件
		if not node:
			return
		# 正常条件
		# 遇到当前层的第一个结点时,levels创建空列表
		if len(levels) == level:
			levels.append([]) 
		# 将node加入当前层的结点列表中
		levels[level].append(node.val)
		# 递归访问左右子树
		bfs_dfs(node.left)
		bfs_dfs(node.right)
	bfs_dfs(root, 0)
	return levels
	

对应的题目:

102.二叉树的层序遍历
107.二叉树的层序遍历Ⅱ
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个结点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针Ⅱ
104.二叉树的最大深度
111.二叉树的最小深度

有一些需要注意的题目:

  • LeetCode 429.N叉树的层序遍历:
    定义中的children是多个child的集合
from collections import deque
"""
# Definition for a Node.
class Node(object):
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution(object):
    def levelOrder(self, root):
        """
        :type root: Node
        :rtype: List[List[int]]
        """
        levels = []
        queue = deque()
        if root: queue.append(root)
        while queue:
            q_size = len(queue)
            level_n = []
            for _ in range(q_size):
                cur = queue.popleft()
                level_n.append(cur.val)
                for child in cur.children:
                    queue.append(child)
            levels.append(level_n)
        return levels

- LeetCode.637. 二叉树的层平均值
每层的平均值为 level_ sum / q_size(但是之前写的代码里面直接除有点问题,要转换为float(level_sum) / float(q_size))

from collections import deque
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def averageOfLevels(self, root):
        """
        :type root: TreeNode
        :rtype: List[float]
        """
        averages = []
        queue = deque()
        if root: queue.append(root)
        while queue:
            q_size = len(queue)
            level_sum = 0
            for _ in range(q_size):
                cur = queue.popleft()
                level_sum += cur.val
                if cur.left: queue.append(cur.left)
                if cur.right: queue.append(cur.right)
            averages.append(float(level_sum) / float(q_size))
        return averages
        

- LeetCode.116. 填充每个结点的下一个右侧结点指针
prev初始化为None,遍历当前层结点的过程中需要prev.next指向cur,且更新prev

from collections import deque
"""
# Definition for a Node.
class Node(object):
    def __init__(self, val=0, left=None, right=None, next=None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        queue = deque()
        if root: queue.append(root)
        while queue:
            q_size = len(queue)
            prev = None		# 初始化为None,如果要初始化为最右侧结点,那么queue需要入队prev.left和prev.right
            for i in range(q_size):
                cur = queue.popleft()
                if prev: prev.next = cur	# prev不为None时指向cur
                prev = cur	# 更新prev
                if cur.left: queue.append(cur.left)
                if cur.right: queue.append(cur.right)
        return root

学习收获:

学习了二叉树的深度优先遍历,前中后序的递归、迭代和统一迭代;学习了二叉树的广度优先遍历,即层序遍历

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值