二叉树(实现基于python)

382 阅读4分钟

树是由n(n≥0)个有限节点组成一个有层次关系的集合。把它叫作树是因为它看起来像一个倒挂的树。它具有以下特点:

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的树;

树的高度、深度、层

  • 节点的高度:节点到叶子节点的最长路径(边数)
  • 节点的深度:根节点到这个节点所经历的边的个数
  • 节点的层数:节点的深度+1
  • 树的高度:根节点的高度 高度从下往上度量(计数起点是0);深度从上往下度量;层数同深度极端不过计数起点是1。

50f89510ad1f7570791dd12f4e9adeb4.webp

二叉树

二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个节点,分别是左子节点和右子节点。不过,二叉树并不要求每个节点都有两个节点。

  • 满二叉树:叶子节点都在最底层,除了叶子节点之外,每个节点都有左右两个子节点。
  • 完全二叉树:叶子节点都在最底两层,最后一层的节点从左到右是连续的,没有断开。

18413c6597c2850b75367393b401ad60.webp

存储二叉树

存储二叉树,有两种方法:一种是基于指针或者引用的二叉链式存储法;一种是基于数组的顺序存储法。
首先看比较简单直观的链式存储法。每个节点有三个字段,其中一个存储数据,另外两个指向左右子节点的指针。只要拎出来根节点,就能通过左右子节点的指针把整棵树串起来。
然后是基于数组的顺序存储法。根节点存储在下标i=1的位置,左子节点存储在下标2*i=2的位置,右子节点存储在2*i+1=3的位置,以此类推。如果当前节点为空,则对应下标置空。所以,如果某棵二叉树是一棵完全二叉树,那用数组存储无疑是最节省内存的一种方式。

14eaa820cb89a17a7303e8847a412330.webp

二叉树的遍历

如何将所有节点都遍历打印出来呢?经典的方法有三种:前序遍历、中序遍历和后序遍历。 ab103822e75b5b15c615b68560cb2416.webp

  • 前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
# 递归
def preorderTraversal(root):
    def preorder(root):
        if root == None: return 
        res.append(root.val)
        preorder(root.left)
        preorder(root.right)
    res = []
    preorder(root)
    return res
# 迭代遍历
def preorderTraversal(root):
    res = []
    stack = []

    while root or stack:
        while root:
            res.append(root.val)
            stack.append(root)
            root = root.left
        root = stack.pop()
        root = root.right
    
    return res
  • 中序遍历是指,对于树中的任意节点来说,先打印这个节点的左子树,再打印它本身,最后打印它的右子树。
# 递归
def inorderTraversal(root):
    def inorder(root):
        if root == None: return 
        inorder(root.left)
        res.append(root.val)
        inorder(root.right)
        return res
    
    res = []
    inorder(root)
    return res
# 迭代遍历
def inorderTraversal(root):
    res = []
    stack = []

    while root or stack:
        while root:
            stack.append(root)
            root = root.left
        root = stack.pop()
        res.append(root.val)
        root = root.right
    
    return res
  • 后序遍历是指,对于树中的任意节点来说,先打印这个节点它的左子树,然后打印它的右子树,最后打印这个节点本身。
# 递归
def postorderTraversal(root):
    def postorder(root):
        if root == None: return 
        postorder(root.left)
        postorder(root.right)
        res.append(root.val)
        return res

    res = []
    postorder(root)
    return res
# 迭代遍历,后序遍历需要主要把根节点放到最后,所以需要一个prev节点记录已遍历过的节点。且如果右子树未遍历,根节点需要二次入栈。
def postorderTraversal(root):
    res = []
    stack = []
    # 记录最新已遍历存入res中的节点
    prev = None

    while root or stack:
        while root:
            stack.append(root)
            root = root.left
        root = stack.pop()
        # 如果不存在右子树或者右子树已经遍历过,则将val存入res中,将prev置为当前节点,且将root置为None。
        if not root.right or prev == root.right:
            res.append(root.val)
            prev = root
            root = None
        # 如果存在右子树,则将当前节点再次压入stack中。(因为根节点要放到最后)
        else:
            stack.append(root)
            root = root.right

    return res
  • 按层遍历:即逐层地,从左到右访问所有节点
# 使用双向队列,遍历过的出列,下一层入列
def levelOrder(root):
    if not root: return []
    res = []
    def level(root):
        queue = collections.deque()
        queue.append(root)
        while queue:
            # 计算当前层级的节点数
            l = len(queue)
            tmp = []
            for i in range(l):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                   queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(tmp)
    level(root)
    return res