代码随想录刷题记录(六)——二叉树(1)

二叉树

1. 理论基础

1.1 二叉树基础

二叉树 一种特殊的树形数据结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。以下是一些相关概念:

  • **节点: ** 在二叉树中,除了根节点之外,每个节点都有且仅有一个父节点。
  • 左子节点和右子节点: 每个节点可以有一个左子节点和一个右子节点。
  • 根节点: 二叉树中没有父节点的节点称为根节点,它是树的起始点。
  • 层数: 根节点的层数为1,它的子节点层数为2,依此类推。
  • **深度和高度:**二叉树中节点的深度是该节点到根节点的边的数量看,其中根节点的深度是1。二叉树的高度是从根节点到最远叶子节点的最长路径上的节点数。
  • 子树: 一棵树的根节点和它所有后代节点组成的树成为该节点的子树。

1.2 特殊二叉树

  • 完全二叉树: 二叉树的每个节点都尽可能地填满左边的子节点,这样的二叉树称为完全二叉树。 就是一颗完全二叉树,同时保证父子节点的顺序关系。
  • 满二叉树: 二叉树的所有层都被完全填满,除了可能的最后一层,且最后一层的所有节点都尽可能地靠左排列,这样的二叉树称为满二叉树。
  • 平衡二叉树: 左右子树的高度差不超过1的二叉树,是一种特殊的二叉搜索树,可以保证操作的效率。
  • 二叉搜索树: 是一个有序树,其中每个节点的值都大于其左子树中所有节点的值,并且小于其右子树中所有节点的值。
  • 二叉堆:
    • 大顶堆: 每个节点的键值都大于或等于其子节点的键值。
    • 小顶堆: 每个节点的键值都小俞或等于其子节点的键值。
  • 霍夫曼树: 用于数据压缩的二叉树,它根据字符出现的概率来构造,使得预期编码长度最小

1.3 二叉树的存储方式

  • 链式存储: 指针存储,通过指针把分布在各个地址的节点串联一起。
  • 顺序存储: 数组存储,在内存是连续分布的。

1.4 二叉树的遍历方式

  • 深度优先遍历(Depth-First Search, DFS): 先往深走,遇到叶子节点再往回走。
    • 前序遍历: (递归法,迭代法)
    • 中序遍历: (递归法,迭代法)
    • 后序遍历: (递归法,迭代法)
      △:这里前中后,指的是中间节点的遍历顺序。
  • 广度优先遍历(Breadth-First Search, BFS): 一层一层的去遍历。
    • 层次遍历: (迭代法)
      在这里插入图片描述

1.5 二叉树的python实现

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

在leetcode中,往往只给出了上述的类来构建二叉树的节点结构,平台会自动处理输入的解析,通常无需用户手动编写构建树的函数,用户只需关注如何实现算法。在这里我们也给出基于上述的节点结构,我们如何构造一个二叉树:

def build_tree(nodes):
    if not nodes:
        return
    root  = TreeNode(nodes[0])
    queue = [root]
    index = 1
    while index < len(nodes):
        node = queue.pop(0)
        if index < len(nodes) and nodes[index] is not None:
            node.left = TreeNode(nodes[index])
            queue.append(node.left)
        index += 1
        if index < len(nodes) and nodes[index] is not None:
            node.right = TreeNode(nodes[index])
            queue.append(node.right)
        index += 1
    return root

2.相关题目

二叉树的递归遍历

题目描述: 分别写出二叉树的前序、中序、后序遍历。
思路: 递归算法的三要素
(1)确定递归函数的参数和返回值
(2)确定终止条件
(3)确定单层递归的逻辑

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) :
        
        def dfs(node): # 递归函数dfs,深度优先搜索。
            if node is None:
                return

            res.append(node.val) # 先存储根节点
            dfs(node.left) # 递归调用dfs,遍历左子树
            dfs(node.right)

        res = [] # 用于存储前序遍历的结果
        dfs(root) # 调用定义好的dfs函数
        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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def dfs(node):
            if node is None:
                return
            
            dfs(node.left)
            res.append(node.val)
            dfs(node.right)
        
        res = []
        dfs(root)
        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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        def dfs(node):
            if node is None:
                return
            
            dfs(node.left)
            dfs(node.right)
            res.append(node.val)
        
        res = []
        dfs(root)
        return res

二叉树的迭代遍历

题目描述: 采用迭代法分别实现前中后序遍历
思路: 在栈与队列中剃刀栈与递归的相似性,这里可以采用栈来实现二叉树的前中后序遍历。
(1)前序遍历:每次先处理中间节点,先将根节点放入栈中,再将右子节点放入栈中,最后放入左子节点。这样能确保出栈的顺序是中左右。注意这里前序遍历访问节点的顺序,与处理节点的顺序是一致的
(2)中序遍历:与前序遍历不同,在中序遍历中,处理顺序和访问顺序不一致,先访问的是二叉树顶部的节点,再逐层访问,直到到达树左面的最底部。再开始处理节点。
(3)后序遍历:调整前序遍历的代码即可。

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) :
        if not root:
            return []
        stack = [root]
        result = []
        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

2. 中序遍历

# 假设 TreeNode 类已经定义如下:
# 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 = []
        
        # 初始化结果列表,用于存储遍历得到的节点值
        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

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) :
        if not root:
            return []
        stack = [root]
        result = []
        while stack:
            node = stack.pop()
            result.append(node.val) # 先处理中间节点
            if  node.left: # 左子节点先入栈
                stack.append(node.left)
            if node.right: # 右子节点后入栈
                stack.append(node.right)

        
        return result

题目描述:
思路:

在这里插入代码片

二叉树的层序遍历

二叉树的层序遍历

题目描述: 给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
思路: 传建一个队列,将根节点入队。当队列非空时,进行循环;每次循环中,创建一个列表level来存储当前层的节点值。取值队列前端的节点,将其值添加到当前层级列表中。将取出节点的左子节点和右子节点添加到队列末尾。将当前层级列表添加到结果列表中。

from collections import deque
# 定义二叉树节点类
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]]:
        # 如果根节点为空,直接返回空列表
        if not root:
            return []
        
        # 使用双端队列来存储待处理的节点
        queue = deque([root])
        
        # 初始化结果列表,用于存储每一层的节点值
        result = []
        
        # 当队列不为空时,继续遍历
        while queue:
            # 初始化一个列表来存储当前层的所有节点值
            level = []
            
            # 循环队列的长度次,因为每层的节点数就是队列的长度
            for _ in range(len(queue)):
                # 从队列前端取出一个节点
                cur = queue.popleft()
                
                # 将当前节点的值添加到当前层的列表中
                level.append(cur.val)
                
                # 如果当前节点有左子节点,将其添加到队列的末尾
                if cur.left:
                    queue.append(cur.left)
                
                # 如果当前节点有右子节点,也将其添加到队列的末尾
                if cur.right:
                    queue.append(cur.right)
            
            # 将当前层的节点值列表添加到结果列表中
            result.append(level)
        
        # 遍历完成后,返回包含所有层级节点值的结果列表
        return result
二叉树的层序遍历(二)

题目描述: 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路: 将上述的结果取反。return result[::-1]

199.二叉树的右视图

题目描述: 给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思路: 层序遍历时,判断是否遍历到单层的最后面的元素,如果是,就放进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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []

        queue = collections.deque([root])
        right_view = []

        while queue:
            level_size = len(queue)

            for i in range(level_size):
                node = queue.popleft()

                if i == level_size - 1: # 判断是否是该层的最后一个
                    right_view。append(node.val) # 一定注意区分节点、节点指针
                
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        
        return right_view
429.N叉树的层序遍

题目描述: 给定一个 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]]:
        if not root:
            return []
        
        result = []
        queue = collections.deque([root])

        while queue:
            level_size = len(queue)
            level = []

            for _ in range(level_size):
                node = queue.popleft()
                level.append(node.val)

                for child in node.children:
                    queue.append(child)

            result.append(level)
        return result
515.在每个树行中找最大值

题目描述: 您需要在二叉树的每一行中找到最大的值。
思路: 先设置一个最大值 max_val = float('-inf') ,然后再媒体弹出节点是,进行比较 max_val = max(max_val, node.val)

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

题目描述: 给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

思路: 使用层序遍历发,每一层中,采用 prev.next = node ,注意,这里是指针指向下一个节点,而不是赋值。然后再进行更新,prev = node


# 定义一个节点类Node,每个节点包含值val、左子节点left、右子节点right和一个指向下一层同位置节点的指针next。
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  # 指向同一层的下一个节点

# 定义一个解决方案类Solution,包含一个方法connect,用于连接二叉树的节点。
class Solution:
    def connect(self, root: 'Node') -> 'Node':
        # 如果根节点为空,直接返回None
        if not root:
            return root
        
        # 使用队列来存储每一层的节点
        queue = collections.deque([root])
        
        # 当队列不为空时,继续处理
        while queue:
            # 记录当前层的节点数量
            level_size = len(queue)
            prev = None  # 初始化前一个节点为None
            
            # 遍历当前层的所有节点
            for i in range(level_size):
                # 从队列中弹出一个节点
                node = queue.popleft()
                
                # 如果前一个节点不为空,将当前节点的next指针指向前一个节点
                if prev:
                    prev.next = node
                
                # 更新前一个节点为当前节点
                prev = node
                
                # 如果当前节点有左子节点,将其加入队列,以便下一层处理
                if node.left:
                    queue.append(node.left)
                
                # 如果当前节点有右子节点,将其加入队列,以便下一层处理
                if node.right:
                    queue.append(node.right)
        
        # 返回连接后的树的根节点
        return root

104.二叉树的最大深度

题目描述: 给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
思路: 层序遍历法,求其层数

111.二叉树的最小深度

题目描述: 给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
思路: 只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。下面给出核心代码

  if not node.left and not node.right:
      return depth

翻转二叉树

题目描述: 翻转一颗二叉树
思路: 实际上是做一个镜像对称,将每个节点的左右子节点翻转,翻转二叉树必须要遍历,前序、后序遍历都可以,中序不可以,因为先左子树,再翻转root左右子树,这时的右子树其实是之前的左子树,最后翻转此时的右子树其实是翻转之前的左子树,这样左子树被翻转了两次。

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 invertTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        root.left, root.right = root.right, root.left
        self.invertTree(root.left)
        self.invertTree(root.right)
        return root

2. (伪)中序遍历: 这里为了解决前面提到的两次翻转同一个节点的问题,固每次迭代root.left

# 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 invertTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        self.invertTree(root.left)
        root.left, root.right = root.right, root.left
        self.invertTree(root.left)
        return root

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 invertTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        self.invertTree(root.left)
        self.invertTree(root.right)
        root.left, root.right = root.right, root.left
        return root

4. 层序遍历法

# 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 invertTree(self, root: TreeNode) -> TreeNode:
        if not root: 
            return None

        queue = collections.deque([root])    
        while queue:
            for i in range(len(queue)):
                node = queue.popleft()
                node.left, node.right = node.right, node.left
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
        return root 
  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值