树-二叉树

非线性表结构:树,二叉树,二叉查找树,平衡二叉查找树、红黑树, 递归树

树的高度,深度,层

  • 高度:从下往上, 从最底层开始计数,并且计数的起点是0
  • 深度:从上往下, 从根节点开始度量,并且计数的起点是0
  • 层: 跟深度计算类似,但是计数起点是1

二叉树

  • 满二叉树 : 叶子节点全都在最底层,除了叶子节点外,每个节点都有左右两个子节点

  • 完全二叉树: 叶子节点都在最底的两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都是满的

二叉树的存储

  • 链式存储法: 每个节点有三个字段,一个存储数据,另外两个指向左右子节点的指针

  • 顺序存储法: 根节点存储在下标i=1的位置,左子节点存储在下标2 i = 2 的位置,右子节点存储在下标2 i + 1的位置, 以此类推。

  • 如果是完全二叉树,那用数组存储无疑是最省内存的一种方式

二叉树的遍历

遍历的时间复杂度是O(n)

  • 前序遍历:对树种任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印右子树
    • 前序非递归的遍历: 采用栈。
    • 1:初始化堆栈
    • 2:把根节点指针入栈
    • 3:当堆栈非空时,循环执行下列步骤(while)
      • 出栈取得栈顶节点,访问该节点,打印
      • 若该节点右孩子节点非空,将该节点右孩子指针入栈
      • 若该节点左孩子节点非空,将该节点左孩子指针入栈
  • 中序遍历:对树种任意节点来说,先打印它的左子树,然后打印它本身,最后打印右字数

  • 后序遍历:对树种任意节点来说,先打印它的左子树,然后打印右子树, 最后打印这个点本身

  • 层序遍历:每一层进行打印

    • 采用队列进行遍历
    • 1:初始化队列
    • 2:把根节点指针入队列
    • 3:当队列非空时,循环执行下列步骤(while)
      • 出队列,获取头节点,访问该节点,打印
      • 若该节点左孩子节点非空,则将该节点左孩子节点入队列
      • 若该节点右孩子节点非空,则将该节点右孩子节点入队列

二叉查找树(二叉搜索树)

二叉查找树: 在树种任意一个节点,其左子树中每个节点的值,都要小于这个节点的值,其右子树每个节点的值都要大于该节点的值。

  • 中序遍历二叉查找树可以输出有序的数据序列,时间复杂度O(n)

  • 二叉查找树,如果根节点的左右子树极度不平衡,就已经退化成链表了,所以查找的时间复杂度变成O(n). 但是这不是我们想要的

    • 二叉查找树中两个极端情况的时间复杂度O(n), O(logn)分别对应退化成链表的情况和完全二叉树。
  • 平衡二叉查找树: 不管是删除还是插入数据,都能较好的保持左右子树较为平衡

    • 平衡二叉查找树的高度接近logn,所以插入,删除,查找的时间复杂度比较稳定是O(logn)

红黑树(平衡二叉查找树)

其实有很多平衡二叉查找树,为什么在工程中大家喜欢用红黑树?

  • Treap, Splay Tree 绝大部分情况下操作效率很高,但无法避免极端情况下时间复杂度的退化

  • AVL树是一种高度平衡的二叉树,查找效率很高,但每次插入删除都有做调整,对于频繁的插入删除操作,使用AVL树代价就很高

  • 红黑树只是近似平衡,在维护平衡的成本上比AVL树要低。此外插入,删除,查找等各种操作性能都比较稳定。工业中更倾向于这种稳定的平衡二叉查找树。