✅DAY13 二叉树 |

一、二叉树的种类

两种主要的形式:满二叉树和完全二叉树

1. 满二叉树Full Binary Tree

满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。

结构:深度为k,有2^k-1个节点的二叉树

2. 完全二叉树 Complete Binary Tree

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。

*** 堆heap就是一棵完全二叉树,同时保证父子节点的顺序关系 

堆属性:在最大堆中,任何一个父节点的值都大于或等于它的子节点;在最小堆中,则相反,父节点的值小于或等于其子节点。

3. 二叉搜索树 Binary Search Tree BST

二叉搜索树是一个有序树,满足:

1. 左子树的值小于根节点的值:对于一个节点 N,其左子树上所有节点的值都小于 N 的值。

2. 右子树的值大于根节点的值:对于一个节点 N,其右子树上所有节点的值都大于 N 的值。

3. 每个子树也是二叉搜索树:这意味着二叉搜索树的性质对每个节点及其子树都成立。

4. 平衡二叉搜索树 Balanced Binary Search Tree, BBST

常见的平衡二叉搜索树类型:

1. AVL 树(Adelson-Velsky and Landis Tree):

        • 每个节点的左右子树高度差最多为 1。

        • 每次插入或删除节点后,通过旋转操作(左旋、右旋、双旋)来重新平衡树。

        • 适合在插入和删除操作较少的情况下使用,因为频繁的旋转操作会带来较大的开销。

2. 红黑树(Red-Black Tree):

        • 是一种近似平衡的二叉搜索树,每个节点附加一个颜色属性(红色或黑色)。

        • 保持了比 AVL 树更松的平衡条件,插入和删除操作的效率更高,因此应用广泛。

        • 常用于 Java 的 TreeMap、TreeSet 以及 Python 的 dict 等实现中。

3. B 树和 B+ 树

        • 不是严格的二叉树,而是多路平衡搜索树,主要用于数据库和文件系统。

        • 每个节点可以有多个子节点,使得树的高度更低,从而更适合磁盘存储结构。

二、二叉树的存储方式

主要有两种:链式存储顺序存储

1. 链式存储:通常使用指针或引用来实现,因此不需要完整的连续空间。

优点

        内存利用率高:只为实际存在的节点分配内存空间,适合表示结构不完整的二叉树(如普通二叉树和二叉搜索树)。

        适用于动态二叉树:可以灵活地进行插入和删除操作。

缺点

        节点访问不直接:要访问特定节点需要从根节点逐步遍历

2. 顺序存储:使用数组将二叉树的节点按层次顺序存储在连续的存储空间中,适合完全二叉树(或接近完全的二叉树),因为完全二叉树的节点位置关系在数组中可以通过简单的公式确定。

存储规则

        • 根节点存储在数组的第一个位置(索引 0 或 1,根据实现不同)。

        • 若节点的索引为 i,则:

                • 左孩子节点的索引为 2*i + 1(或 2*i,基于索引起始位置)。

                • 右孩子节点的索引为 2*i + 2(或 2*i + 1)。

                • 父节点的索引为 (i - 1) // 2。

优点

        • 节点访问高效:数组存储,访问节点速度快,适合用于完全二叉树结构。

缺点

        • 空间浪费:不适合非完全二叉树或稀疏二叉树,因为这会导致大量数组空间浪费。

        • 扩展性差:在动态插入和删除节点时不灵活,通常用于静态结构的二叉树。

三、二叉树的遍历方式

二叉树的遍历方式主要有四种:前序遍历中序遍历后序遍历层序遍历。其中前序、中序和后序遍历是深度优先遍历(DFS),层序遍历是广度优先遍历(BFS)。

深度优先遍历:前中后,指的就是中间节点的遍历顺序

  • 前序遍历(递归法,迭代法)
  • 中序遍历(递归法,迭代法)
  • 后序遍历(递归法,迭代法)

广度优先遍历

  • 层次遍历(迭代法)

四、二叉树的定义

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

五、二叉树的递归遍历

1. 前序

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        def dfs(node):
            if node is None:
                return 
            ans.append(node.val)
            dfs(node.left)
            dfs(node.right)
        
        dfs(root)
        return ans

2. 中序 

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        def dfs(node):
            if node is None:
                return 
            dfs(node.left)
            ans.append(node.val)
            dfs(node.right)
        
        dfs(root)
        return ans

3. 后序

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans= []
        def dfs(node):
            if node is None:
                return 
            dfs(node.left)
            dfs(node.right)
            ans.append(node.val)
        dfs(root)
        return ans

六、二叉树的迭代遍历

1. 先序

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        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.中序

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.后序

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        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[::-1]

七、层序遍历

层序遍历(广度优先遍历)是一种按照从上到下、从左到右的顺序遍历二叉树的方式。使用队列来辅助实现层序遍历,因为队列遵循先进先出(FIFO)的特性,可以很好地支持按层遍历。

102.二叉树的层序遍历

# 迭代解法
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:  # 空树直接返回空列表
            return []
        
        queue = collections.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  # 返回按层的节点值
#递归解法
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        
        levels = []  # 存储每一层的节点值

        def traverse(node, level):
            if not node:
                return

            # 当 levels 长度等于当前 level 时,添加一个新的层级列表
            if len(levels) == level:
                levels.append([])

            # 将当前节点的值添加到当前层级对应的列表中
            levels[level].append(node.val)

            # 递归地处理左子树和右子树,并增加层级
            traverse(node.left, level + 1)
            traverse(node.right, level + 1)

        # 从根节点开始递归,初始层级为 0
        traverse(root, 0)
        return levels

107.二叉树的层次遍历 II

上一题解法返回result[::-1]

199.二叉树的右视图

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        
        queue = deque([root])  # 初始化队列,将根节点入队
        result = []  # 用于存储右视图的节点值

        while queue:
            level_length = len(queue)  # 当前层的节点数
            for i in range(level_length):
                node = queue.popleft()  # 弹出当前层的节点

                # 如果是该层的最后一个节点,添加到结果中
                if i == level_length - 1:
                    result.append(node.val)

                # 将左右子节点加入队列,以便遍历下一层
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)

        return result

637.二叉树的层平均值 

class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        if not root:
            return []
        queue = collections.deque([root])
        average = []

        while queue:
            level_size = len(queue)
            level_sum = 0
            for i in range(level_size):
                node = queue.popleft()
                level_sum += node.val
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            average.append(level_sum/level_size)
        return average

429.N叉树的层序遍历

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.在每个树行中找最大值

class Solution:
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []

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

        while queue:
            level_size = len(queue)
            max_val = float('-inf')
            for _ in range(level_size):
                node = queue.popleft()
                max_val = max(max_val, node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(max_val)
        return result

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

题目:每行的左node指向右node,最右边的指向null

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if not root:
            return root
        
        queue = collections.deque([root])
        
        while queue:
            level_size = len(queue)
            prev = None

            for i in range(level_size):
                node = queue.popleft()
                # 如果已经有指向,使得前一个指向新的
                if prev:
                    prev.next = node
                # 没有就赋值到当前node上
                prev = node
                if node.left:
                    queue.append(node.left)
                
                if node.right:
                    queue.append(node.right)
        
        return root
        

104.二叉树的最大深度

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return root
        
        queue = collections.deque([root])
        depth = 0
        while queue:
            depth += 1

            for i in range(len(queue)):
                node = queue.popleft()
               
                if node.left:
                    queue.append(node.left)
                
                if node.right:
                    queue.append(node.right)
        return depth
        

111.二叉树的最小深度

只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        depth = 0
        queue = collections.deque([root])
        
        while queue:
            depth += 1 
            for _ in range(len(queue)):
                node = queue.popleft()
                
                if not node.left and not node.right:
                    return depth
            
                if node.left:
                    queue.append(node.left)
                    
                if node.right:
                    queue.append(node.right)

        return depth

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值