代码随想录算法训练营第十三天 | 二叉树理论基础、递归遍历、迭代遍历、统一迭代、层序遍历

一、二叉树理论基础

文章讲解:代码随想录 (programmercarl.com)——二叉树理论基础
视频讲解:关于二叉树,你该了解这些!| 二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili

1. 种类

满二叉树:节点数量2^k -1
完全二叉树:底部从左到右。
二叉搜索树:左子树所有节点小于中间节点,右子树所有节点大于中间节点,时间复杂度为log(n)。
平衡二叉搜索树:左子树和右子树高度差不能超过1。

2. 存储方式

链式存储:为一个链表,指针指向两个子节点。
线式存储:用一个字符数组保存二叉树,左子节点为 2\times i+1,右子节点为 2\times i+2

3. 遍历方式

递归遍历(深度优先搜索):一直搜到终点再回头换方向,一般用递归实现,也可以用迭代法解决。常见方式:前中后序遍历,前序遍历——中左右,中序遍历——左中右,后续遍历——左右中,什么序中在哪
层序遍历(广度优先搜索):一层一层或一圈一圈遍历,用迭代法实现。常见方式:层序遍历。

4. 定义方式

class TreeNode:
    def __init__(self, val, left = None, right = None):
        self.val = val
        self.left = left
        self.right = right

二、递归遍历(必须掌握)

题目链接:144. 二叉树的前序遍历 - 力扣(LeetCode)94. 二叉树的中序遍历 - 力扣(LeetCode)145. 二叉树的后序遍历 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——递归遍历
视频讲解:每次写递归都要靠直觉? 这次带你学透二叉树的递归遍历!| LeetCode:144.前序遍历,145.后序遍历,94.中序遍历_哔哩哔哩_bilibili

递归三部曲:
1. 确定递归函数的参数和返回值;
2. 确定终止条件;
3. 确定单层递归的逻辑。

# 前序遍历
# 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(node):
            if node is None:
                return

            res.append(node.val)  # 中
            dfs(node.left)        # 左
            dfs(node.right)       # 右

        dfs(root)
        return res
# 中序遍历
# 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 = []

        # 确定终止条件
        def dfs(node):
            if node is None:
                return

            dfs(node.left)        # 左
            res.append(node.val)  # 中
            dfs(node.right)       # 右
            
        dfs(root)
        return res
# 后序遍历
# 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]:
        res = []

        # 确定终止条件
        def dfs(node):
            if node is None:
                return

            dfs(node.left)        # 左
            dfs(node.right)       # 右
            res.append(node.val)  # 中
            
        dfs(root)
        return res

三、迭代遍历

 题目链接:144. 二叉树的前序遍历 - 力扣(LeetCode)94. 二叉树的中序遍历 - 力扣(LeetCode)145. 二叉树的后序遍历 - 力扣(LeetCode)

145. 二叉树的后序遍历 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——迭代遍历
视频讲解:写出二叉树的非递归遍历很难么?这次让你不再害怕非递归!|二叉树的非递归遍历 | 二叉树的遍历迭代法 | 前序与中序_哔哩哔哩_bilibili写出二叉树的非递归遍历很难么?这次再带你写出中序遍历的迭代法!|二叉树的非递归遍历 | 二叉树的遍历迭代法_哔哩哔哩_bilibili

1. 前序遍历

思路:用栈实现,前序遍历先放右子节点,再放左子节点。

# 迭代遍历——前序遍历
# 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 []

        # 定义一个栈
        stack = [root]
        res = []
        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

2. 后序遍历

思路:在前序遍历基础上,左右做一个颠倒,此时为中右左,最后再来一个翻转即可。

# 迭代遍历——后序遍历
# 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]: 
        # 根节点为空则返回空列表
        if not root:
            return []

        # 定义一个栈
        stack = [root]
        res = []
        while stack:
            node = stack.pop()

            # 中节点加入栈
            res.append(node.val)

            # 左子节点入栈
            if node.left:
                stack.append(node.left)

            # 右子节点入栈
            if node.right:
                stack.append(node.right)

        return res[::-1]

3. 中序遍历

思路:首先一路向左,加入栈,当遍历到根左子节点的时候,弹出加入数组,弹出中节点,再去找中节点是否有右子节点,不断重复。

Note:用指针遍历节点,用栈记录指针遍历过的元素

# 迭代遍历——中序遍历
# 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]:
        # 根节点为空则返回空列表
        if not root:
            return []
        
        # 定义一个栈,记录指针遍历的元素
        stack = []
        res = []

        # 定义一个指针,为根节点 
        cur = root

        # 终止条件:cur为空或栈为空
        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

四、统一迭代

文章讲解:代码随想录 (programmercarl.com)——统一迭代

五、层序遍历

文章讲解:代码随想录 (programmercarl.com)——层序遍历
视频讲解:讲透二叉树的层序遍历 | 广度优先搜索 | LeetCode:102.二叉树的层序遍历_哔哩哔哩_bilibili

思路:借助队列保存每一层遍历过的元素。第一层元素加队列,记录当前层size,弹出size个元素,并加入其左右节点元素,记录第二层size,弹出第一个元素加入其左右子节点元素,size-1,再弹出一个元素,并将其左右子节点加入队列,size-2,直到size=0,记录下一层size,重复弹出记录的操作。

102.二叉树的层序遍历

题目链接:102. 二叉树的层序遍历 - 力扣(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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            
            # 将当前层元素加入结果
            res.append(vator)

        return res

107.二叉树的层次遍历 II

题目链接:107. 二叉树的层序遍历 II - 力扣(LeetCode)

Note:相当于给上一道题反转一下。

# 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 levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            
            # 将当前层元素加入结果
            res.append(vator)

        return res[::-1]

199.二叉树的右视图

题目链接:199. 二叉树的右视图 - 力扣(LeetCode)

Note:取每一行数组最后一个元素

# 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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            
            # 将当前层元素加入结果
            res.append(vator[-1])

        return res

637.二叉树的层平均值

题目链接:637. 二叉树的层平均值 - 力扣(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
import numpy as np
class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            
            # 将当前层元素加入结果
            means = np.mean(vator)
            res.append(means)

        return res

429.N叉树的层序遍历

题目链接:429. N 叉树的层序遍历 - 力扣(LeetCode)

Note:改成n个子节点,此时不能用左右,需要遍历

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                for child in cur.children:
                    que.append(child)
            
            # 将当前层元素加入结果
            res.append(vator)

        return res

515.在每个树行中找最大值

题目链接:515. 在每个树行中找最大值 - 力扣(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 largestValues(self, root: Optional[TreeNode]) -> List[int]:
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个数组放当前层元素
            vator = []

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()
                # 将当前节点值放到当前层的结果中
                vator.append(cur.val)

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            
            # 将当前层元素加入结果
            max1 = max(vator)
            res.append(max1)

        return res

116.填充每个节点的下一个右侧节点指针

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

"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        """
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义每一层开始之前,还没有前一个节点
            prev = None

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()

                # 如果不为空,说明不是当前层第一个节点
                if prev:
                    # 将前一个节点的 next 指针指向当前节点,这样就在每一层形成了一个链表
                    prev.next = cur

                # 更新 prev 为当前节点,以便在下一次迭代中使用。
                prve = cur

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            

        return root

117.填充每个节点的下一个右侧节点指针II

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

Note:代码和前一道题一模一样

"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        """
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        # 定义一个队列
        que = collections.deque()
        res = []

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义每一层开始之前,还没有前一个节点
            prev = None

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()

                # 如果不为空,说明不是当前层第一个节点
                if prev:
                    # 将前一个节点的 next 指针指向当前节点,这样就在每一层形成了一个链表
                    prev.next = cur

                # 更新 prev 为当前节点,以便在下一次迭代中使用。
                prve = cur

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            

        return root

104.二叉树的最大深度

题目链接:104. 二叉树的最大深度 - 力扣(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 maxDepth(self, root: Optional[TreeNode]) -> int:
        # 定义一个队列
        que = collections.deque()
        # 初始化深度
        depth = 0

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个深度
            depth += 1

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            

        return depth

111.二叉树的最小深度

题目链接:111. 二叉树的最小深度 - 力扣(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 minDepth(self, root: Optional[TreeNode]) -> int:
        # 定义一个队列
        que = collections.deque()
        # 初始化深度
        depth = 0

        # 如果根节点不为空则加入队列
        if root is not None:
            que.append(root)
        
        if not root:
            return 0

        # 遍历终止条件为队列中没有元素了
        while que:
            # 记录当前层节点数
            size = len(que)

            # 定义一个深度
            depth += 1

            for i in range(size):
                # 从队列中弹出当前节点
                cur = que.popleft()

                # 判断是否有左右子节点
                if not cur.left and not cur.right:
                    return depth

                # 将节点左右子节点加入队列
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
            

        return depth

你可能感兴趣的:(代码随想录算法训练营,算法,数据结构)