代码随想录算法训练营Day11 | 二叉树的递归遍历,迭代遍历,统一迭代,层序遍历

目录

二叉树的递归遍历

二叉树的迭代遍历

二叉树的统一迭代

二叉树的层序遍历


二叉树的递归遍历

题目链接:

144. 二叉树的前序遍历 - 力扣(LeetCode)

94. 二叉树的中序遍历 - 力扣(LeetCode)

145. 二叉树的后序遍历 - 力扣(LeetCode)

文章讲解:代码随想录

解题卡点:第一次见二叉树,学习思路

        二叉树主要有两种遍历方式:

        深度优先遍历:先往深走,遇到叶子节点再往回走。①前序遍历 (递归法,迭代法);②中序遍历 (递归法,迭代法);③后序遍历 (递归法,迭代法)。

        广度优先遍历:一层一层的去遍历。层次遍历 (迭代法)。

        这里的前中后,指的是中间节点的遍历顺序。

        递归的三要素写法:

        ①确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么,进而确定递归函数的返回类型。

        ②确定终止条件:写完了递归算法,运行的时候经常会遇到栈溢出的错误,这是没写终止条件或者终止条件写的不对。操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

        ③确定单层递归的逻辑:确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

        以前序遍历为例:

        ①确定递归函数的参数和返回值:函数传入node获取当前节点信息,函数不需要有返回值,只需return跳出当前递归就行。

        ②确定终止条件:在递归的过程中,若当前遍历的节点为None,那么本层递归就要结束。

        ③确定单层递归的逻辑:前序遍历是中左右的顺序,所以在单层递归的逻辑,要先取中节点的数值,随后递归的进行左节点和右节点。

# 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

class Solution:
    # 深度优先_递归前序遍历
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        def dfs_preorder(node):
            if node is None:
                return
            res.append(node.val)
            dfs_preorder(node.left)
            dfs_preorder(node.right)
        dfs_preorder(root)
        return res

    # 深度优先_递归中序遍历
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        def dfs_inorder(node):
            if node is None:
                return
            dfs_inorder(node.left)
            res.append(node.val)
            dfs_inorder(node.right)
        dfs_inorder(root)
        return res

    # 深度优先_递归后序遍历
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        def dfs_postorder(node):
            if node is None:
                return
            dfs_postorder(node.left)
            dfs_postorder(node.right)
            res.append(node.val)
        dfs_postorder(root)
        return res

二叉树的迭代遍历

题目链接:

144. 二叉树的前序遍历 - 力扣(LeetCode)

94. 二叉树的中序遍历 - 力扣(LeetCode)

145. 二叉树的后序遍历 - 力扣(LeetCode)

文章讲解:代码随想录

解题卡点:第一次见二叉树,学习思路

        前序遍历:中左右,每次先处理中间节点。先将根节点放入栈中,然后将右子节点加入栈,再加入左子节点,确保出栈的顺序是中左右。

        后序遍历:调整前序遍历的子节点顺序,再反转结果列表即可。

        中序遍历:前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,要访问的元素和要处理的元素顺序是一致的。而中序遍历左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点,造成了处理顺序和访问顺序不一致的问题。

        因此中序遍历需要用指针的遍历来帮助访问节点,用栈来处理节点上的元素。

        本题思路:

        ①指针cur=root,然后不断访问cur.left,找到整个二叉树最左的节点。在这个过程中,路径上的所有节点都要入栈。这其实是为了记录一层层的父节点。

        ②如果cur为空,说明当前这一层级的二叉树已经处理完了,就从栈中弹出一个待处理的节点,令cur指向它,再把它的数值加入结果集。

        ③接下来再次移动指针,使cur=cur.right,开始处理cur的右节点。

        ④此时就开始了下一次大循环。如果cur不为空,就一路去找最左的节点,和之前一样的逻辑。

# 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

class Solution:
    # 深度优先_迭代前序遍历
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        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

    # 深度优先_迭代中序遍历
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        res = []
        stack = []
        cur = root
        while cur or stack:
            if cur:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res

    # 深度优先_迭代后序遍历
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        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

二叉树的统一迭代

题目链接:

144. 二叉树的前序遍历 - 力扣(LeetCode)

94. 二叉树的中序遍历 - 力扣(LeetCode)

145. 二叉树的后序遍历 - 力扣(LeetCode)

文章讲解:代码随想录

解题卡点:第一次见二叉树,学习思路

        前中后序的统一风格迭代法代码。

        由于无法同时解决访问节点 (遍历节点) 和处理节点 (将元素放进结果集) 不一致的情况,那么就将访问的节点放入栈中,把要处理的节点也放入栈中并做好标记。

        ①空指针标记法:要处理的节点放入栈之后,紧接着放入一个空指针作为标记。

        总结一下上面的实现过程:
        ①在栈里放入root节点,当栈不为空时,弹出栈顶节点。接下来有两种情况:

        ②如果弹出的node不为None,按照遍历顺序的逆序,依次向栈里加入左中右三个节点,加入中节点时后面加一个None。

        ③如果弹出的node为None,就继续弹出一个节点,将该节点的val加入结果集。

        可以看到只在②中入栈,在③中进行记录,以此实现了迭代代码的统一。

        上面的判断中,很重要的一个点是判断栈顶元素是否为空,原因在与压栈的时候对节点进行了标记,说明该节点是中间节点,如果栈顶元素为空,说明用NULL标记的这个节点的左节点已经处理了或没有左子节点,现在可以处理中间节点了,而如果栈顶元素不为空,说明该节点还不是还有左子节点,因此需要将其子节点继续压栈。假设遍历到某叶子节点,因为它没有子节点,所以按照一定的顺序压栈后,栈顶元素就是NULL,这是关键!!!

# 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

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:

                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

    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:

                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

    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:

                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

        ②布尔标记法:加一个布尔值跟随每个节点,False (默认值) 表示需要为该节点和它的左右子节点安排在栈中的位次,True表示该节点的位次之前已经安排过了,可以放入结果数组中。

# 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

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = [(root, False)] if root else []
        while stack:
            node, visited = stack.pop()
            if visited:
                res.append(node.val)
                continue
        
            if node.right:
                stack.append((node.right, False))
            
            stack.append((node, True))

            if node.left:
                stack.append((node.left, False))
        
        return res

二叉树的层序遍历

题目链接:

102. 二叉树的层序遍历 - 力扣(LeetCode)

107. 二叉树的层序遍历 II - 力扣(LeetCode)

199. 二叉树的右视图 - 力扣(LeetCode)

637. 二叉树的层平均值 - 力扣(LeetCode)

429. N 叉树的层序遍历 - 力扣(LeetCode)

515. 在每个树行中找最大值 - 力扣(LeetCode)

116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)

117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)

104. 二叉树的最大深度 - 力扣(LeetCode)

111. 二叉树的最小深度 - 力扣(LeetCode)

文章讲解:代码随想录

解题卡点:第一次见二叉树,学习思路

        队列→先进先出→符合一层一层遍历的逻辑

        栈→先进后出→适合递归的逻辑        

        层序遍历就是从上到下,从左到右地输出每一层。使用队列来完成层序遍历,初始化时放入root节点。每出队一个节点,将其val加入结果集,然后再将它的左右节点入队。

# 102. 二叉树的层序遍历
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        res = []
        queue = collections.deque([root])
        while queue:
            level = []
            level_size = len(queue)
            for _ in range(level_size):
                cur = queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(level)
        return res

        102是层序遍历基础,后面的题都是对102的应用。其中需要注意429N叉树的寻找子节点,116的节点next链接,111树最小深度的条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值