【leetcode】二叉树分类、遍历方式、解题思路总结

二叉树分类

  • 满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。即深度为k,有2^k-1个节点的二叉树。
  • 完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
  • 二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。中序遍历下,二叉搜索树节点的数值是有序序列。

  • 平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

二叉树存储方式

  • 链式存储:通过指针把分布在各个地址的节点串联一起。
  • 顺序存储:顺序存储的元素在内存是连续分布的,如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

递归函数设计步骤

  1. 确定递归函数的函数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

二叉树的遍历

深度优先遍历

深度优先遍历:先往深走,遇到叶子节点再往回走。前中后遍历,其实指的是中间节点的遍历顺序,遍历的差异点在于访问顺序和处理顺序的不同。深度优先遍历需要利用栈存放访问过的节点、未访问过的(待处理的)节点

深入理解:

  1. 前中后序遍历是遍历二叉树过程中处理每一个节点的三个特殊时间点。二叉树的所有问题,就是让我们在前中后序位置注入巧妙的代码逻辑,去达到题目的目的,我们只需要单独思考每一个节点应该做什么,其他的不用管,抛给二叉树遍历框架,递归会在所有节点上做相同的操作
  2. 前序位置的代码执行是自顶向下的,而后序位置的代码执行是自底向上的。前序位置的代码只能从函数参数中获取父节点传递来的数据,而后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据

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

递归法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def preorderTraversal(self, root):
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, r):
        if cur is None:
            return
        r.append(cur.val)
        self.traversal(cur.left, r)
        self.traversal(cur.right, r)

迭代法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def preorderTraversal(self, root):
        res = []
        if root is None:
            return res
        # 同时记录访问过的节点和未访问过节点,访问过节点后用None跟随表示
        stack = [root]
        while stack:
            node = stack.pop()
            if node is not None:
                if node.right:
                    stack.append(node.right)
                if node.left:
                    stack.append(node.left)
                stack.append(node)
                stack.append(None)
            else:
                res.append(stack.pop().val)
        return res

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

递归法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def preorderTraversal(self, root):
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, r):
        if cur is None:
            return
        self.traversal(cur.left, r)
        r.append(cur.val)
        self.traversal(cur.right, r)

迭代法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def inorderTraversal(self, root):
        res = []
        if root is None:
            return res
        # 同时记录访问过的节点和未访问过节点,访问过节点后用None跟随表示
        stack = [root]
        while stack:
            node = stack.pop()
            if node is not None:
                if node.right:
                    stack.append(node.right)
                stack.append(node)
                stack.append(None)
                if node.left:
                    stack.append(node.left)
            else:
                res.append(stack.pop().val)
        return res

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

递归法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def preorderTraversal(self, root):
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, r):
        if cur is None:
            return
        self.traversal(cur.left, r)
        self.traversal(cur.right, r)
        r.append(cur.val)

迭代法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def postorderTraversal(self, root):
        res = []
        if root is None:
            return res
        # 同时记录访问过的节点和未访问过节点,访问过节点后用None跟随表示
        stack = [root]
        while stack:
            node = stack.pop()
            if node is not None:
                stack.append(node)
                stack.append(None)
                if node.right:
                    stack.append(node.right)
                if node.left:
                    stack.append(node.left)
            else:
                res.append(stack.pop().val)
        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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        levels = []

        def traverse(node, level):
            if not node:
                return
            if len(levels) == level:
                levels.append([])
            levels[level].append(node.val)
            traverse(node.left, level + 1)
            traverse(node.right, level + 1)

        traverse(root, 0)
        return levels

迭代法

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def levelOrder(self, root):
        from collections import deque
        res = []
        if root is None:
            return res
        queue = deque([root])
        while queue:
            tmp_res = []
            tmp_num = len(queue)
            for i in range(tmp_num):
                node = queue.popleft()
                tmp_res.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(tmp_res)
        return res

二叉树问题的解题思路

二叉树解题的思维模式分两类,有的题可能两种思路都可以解决,有的题只能用其中一种解决:

  1. 是否可以通过遍历一遍二叉树得到答案?如果可以,用一个traversal函数,此时递归函数一般不带返回值,依靠更新外部变量来计算结果,这叫遍历的思维模式(如果遇到找到一个合法答案后终止递归的问题,依旧推荐traversal函数就起到遍历的作用,不要带返回值,至于是否找到了答案,由外部变量控制
  2. 是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案?如果可以,首先写出这个递归函数的定义,此时递归函数一般会有返回值,返回值是子问题的计算结果,然后在后序位置做文章,这叫分解问题的思维模式
  3. 无论使用哪一种思维模式,你都要明白二叉树的每一个节点需要做什么,需要在什么位置(前中后序)做

动态规划/DFS/回溯算法都可以看做二叉树问题的扩展,只是它们的关注点不同:

  • 动态规划算法属于分解问题的思路,它的关注点在整棵「子树」
  • 回溯算法属于遍历的思路,它的关注点在节点间的「树枝」
  • DFS 算法属于遍历的思路,它的关注点在单个「节点」
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值