定义

在计算机科学中,树(英语:tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

术语

  1. 节点的度:一个节点含有的子树的个数称为该节点的度(根结点1的度为2);

  2. 树的度:一棵树中,最大的节点度称为树的度(二叉树的度为2);

  3. 叶节点或终端节点:度为零的节点(节点5、6、7、8);

  4. 非终端节点或分支节点:度不为零的节点(节点1、2、3、4);

  5. 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点(节点2的父节点为1,根结点1没有父节点);

  6. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点(节点2的孩子节点为4、5);

  7. 兄弟节点:具有相同父节点的节点互称为兄弟节点(节点4和5、节点6和7);

  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推(如图);

  9. 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0(如图);

  10. 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0(如图);

  11. 堂兄弟节点:父节点在同一层的节点互为堂兄弟(节点4和6);

  12. 节点的祖先:从根到该节点所经分支上的所有节点(节点8的祖先节点为:1、2、4);

  13. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙(节点2的子孙为4、5、8)。

性质

  1. 每个节点都只有有限个子节点或无子节点;

  2. 没有父节点的节点称为根节点;

  3. 每一个非根节点有且只有一个父节点;

  4. 除了根节点外,每个子节点可以分为多个不相交的子树;

  5. 树里面没有环路(cycle)

分类

  1. 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;

  2. 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;

    • 二叉树:每个节点最多含有两个子树的树称为二叉树;

      • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;

        • 满二叉树:所有叶节点都在最底层的完全二叉树;

      • 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;

      • 排序二叉树(二叉查找树(英语:Binary Search Tree)):也称二叉搜索树、有序二叉树;

    • 霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树;

    • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个子树。

存储

  1. 顺序存储(数组):紧凑、CPU缓存亲和

    • 完全二叉树(满二叉树)

  2. 链式存储(二叉链表):避免数组存储浪费空间的问题

    • 普通二叉树

  3. 链式存储(三叉链表):存储父节点指针

遍历

以二叉树为例

  1. 深度优先(DFS)

    • 前序(根-左-右)

    • 中序(左-根-右)

    • 后序(左-右-根)

  2. 广度优先(BFS)

DFS

递归框架

递归写法非常简单,如下所示

traverse(root) {
    if not root:
        return

    # print(root.val)       # 前序
    traverse(root.left)
    # print(root.val)       # 中序
    traverse(root.right)
    # print(root.val)       # 后序 
}

迭代

前序

利用栈先进后出的特性,

  1. 使用根节点初始化栈;

  2. 当栈不为空(确定终止条件)

    1. 使栈顶节点出栈(第一次为根结点),取节点值放入返回列表中;

    2. 如果不为叶子节点(存在左孩子或右孩子或都有),依次往栈里放入右孩子节点、左孩子节点

    3. 如果为叶子节点,则不入栈

class Solution:
    def preorderTraversal(self, root: 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

中序

与递归法类似,模拟函数栈的出入过程

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        current = root
        while current or stack:
            while current:
                stack.append(current)
                current = current.left
            current = stack.pop()
            res.append(current.val)
            current = current.right
        return res

后序

前序遍历的逆序输出

class Solution(object):
    def postorderTraversal(self, root):

        if root is None:
            return []

        stack, res = [root, ], []
        while stack:
            root = stack.pop()
            res.append(root.val)
            if root.left is not None:
                stack.append(root.left)
            if root.right is not None:
                stack.append(root.right)

        return res[::-1]

BFS

迭代

广度优先遍历又称为层次遍历(从根节点向叶子节点、从左到右)

  1. 根节点为第一层,深度为0。初始化一个长度为0的结果列表。从根结点开始:

    1. 如果当前的结果列表长度等于深度,则加一层(对应列表里面加一个空列表)用来保存该层的值

    2. 如果当前的结果列表长度不等于深度,则把当前节点的值加入到该层的列表中

  2. 继续遍历当前节点的左孩子,并使深度+1

  3. 继续遍历当前节点的右孩子,并使深度+1

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []

        def helper(root, depth):
            if not root: return 
            if len(res) == depth:
                res.append([])
            res[depth].append(root.val)
            helper(root.left, depth + 1)
            helper(root.right, depth + 1)
        helper(root, 0)
        return res

二叉树(Binary Tree)

完全二叉树(Complete tree)

性质

判断一棵树是否是完全二叉树的思路

  1. 如果树为空,则直接返回错

  2. 如果树不为空:层序遍历二叉树

    1. 如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列;

    2. 如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;

    3. 如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空;则该节点之后的队列中的结点都为叶子节点;该树才是完全二叉树,否则就不是完全二叉树;

满二叉树(Full Tree)

二叉搜索树

性质

  1. 若任意结点的左子树不空,则左子树上所有结点的值均不大于它的根结点的值。

  2. 若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值。

  3. 任意结点的左、右子树也分别为二叉搜索树。

复杂度

  • 平均:O(logN)

  • 最坏:O(N),极端情况下退化为链表

平衡二叉树

为了保证树不至于太倾斜,尽量保证两边平衡

性质

  1. 平衡二叉树要么是一棵空树

  2. 要么保证左右子树的高度之差不大于 1

  3. 子树也必须是一颗平衡二叉树

题型

  • 结构(构造树、对称树、相似树、翻转、平衡)

  • 节点(高度、深度、层级、查找、祖先)

  • 路径

参考

Last updated