算法刷题打卡012 | 二叉树基础遍历

递归遍历

复习递归函数三要素:递归函数的参数和返回值、终止条件以及单层递归的逻辑。

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

# 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]:
        result = []
        # 递归法
        def dfs(node):
            if not node:
                return  # 节点为空,返回
            result.append(node.val)
            dfs(node.left)
            dfs(node.right)
            # return result  # result用全局变量,不用返回值
        
        
        dfs(root)
        return result
            

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

# 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]:
        result = []
        # 递归法
        def dfs(node):
            if not node:
                return  # 节点为空,返回
            dfs(node.left)
            result.append(node.val)
            dfs(node.right)
            # return result  # result用全局变量,不用返回值
        
        dfs(root)
        return result
            

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

# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        result = []
        # 递归法
        def dfs(node):
            if not node:
                return  # 节点为空,返回
            dfs(node.left)
            dfs(node.right)
            result.append(node.val)
            # return result  # result用全局变量,不用返回值

        dfs(root)
        return result
            

三种遍历的写法是统一的,掌握其中一种递归逻辑,另外两种遍历方式只需要更改单层递归的遍历顺序即可,时间复杂度都是O(n)。

迭代遍历

迭代法中使用额外的栈空间模拟递归流程,其中前序和后序遍历的代码逻辑基本相同:

# 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]:
        # 迭代法
        stack = []
        result = []
        if root is None:
            return result
        stack.append(root)
        while stack:
            cur = stack.pop()
            result.append(cur.val)
            if cur.right is not None:
                stack.append(cur.right)
            if cur.left is not None:
                stack.append(cur.left)
        return result
# 后序遍历
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 迭代法
        stack = []
        result = []
        if root is None:
            return result
        stack.append(root)
        while stack:
            cur = stack.pop()
            result.append(cur.val)
            if cur.left is not None:
                stack.append(cur.left)
            if cur.right is not None:
                stack.append(cur.right)
        result.reverse()
        return result

由于栈先入后出,要想按前序的中左右顺序将节点值加入result数组,单层递归逻辑中需要先将右节点入栈,同时注意空节点不入栈;后序遍历并不意味着根节点最后入栈,而是巧妙地通过前序遍历后将最后的结果数组反转实现,反转前result中的遍历顺序是中右左,反转过来即为后序的左右中。

而中序遍历会稍微不同。由于遍历节点和处理节点(收集结果)的顺序不一致,需要将两种逻辑分开,增加一个指针用于遍历,当遇到节点的左子节点为空就可以开始从栈中收集结果(因为左子节点为空时,前一个入栈的节点就对应当前节点,左为空,结果可以跳过,下一步就是收集当前节点的值,然后遍历右节点):

# 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]:
        #  迭代法(代码随想录)
        stack, result = [], []
        if not root:
            return result
        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
        
        return result

 用具体例子模拟一下就能想明白这个过程,这个迭代思路确实很奇妙!

统一迭代

用中序遍历的逻辑将迭代法统一起来,增加一个标记:要处理的节点放入栈之后,紧接着放入一个空指针作为标记。下面贴的是代码随想录中的中序遍历代码,其他两种遍历顺序只需要调整非空节点中的添加节点顺序。跟随代码逻辑可以理解这种做法,但自己其实是不容易想到的,死记代码容易有错漏,还得多看多理解,将思路融汇于心才行。

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        result = []
        st = []
        if root:
            st.append(root)
        while st:
            node = st.pop()
            if node != None:
                if node.right: #添加右节点(空节点不入栈)
                    st.append(node.right)
                
                st.append(node) #添加中节点
                st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。
                
                if node.left: #添加左节点(空节点不入栈)
                    st.append(node.left)
            else: #只有遇到空节点的时候,才将下一个节点放进结果集
                node = st.pop() #重新取出栈中元素
                result.append(node.val) #加入到结果集
        return result

二叉树遍历比较基础,递归法很好掌握,迭代法需要多熟悉。

你可能感兴趣的:(刷题,算法,数据结构)