二叉树

概念定义

节点高度: 节点到叶子节点的最长路径(边数)

节点深度: 根节点到这个接点所经历的边的个数

节点层: 节点的深度 + 1

树的高度: 根节点的高度

二叉树

每个节点最多有两个叉,分别是左子节点和右子节点,并不一定每个节点都有两个子节点。

满二叉树: 叶子节点全在最底层,除了叶子节点每个节点都有左右两个子节点,这种二叉树就是满二叉树。

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

二叉树的存储

  1. 基于指针或二叉链式存储法
  2. 基于数组的顺序存储法(完全二叉树存储优先方式)
链式存储法

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

顺序存储法

根节点存储在下标 i=1 的位置,左节点存储在 2 * i = 2的位置,右节点存储在 2 * i + 1 = 3 的位置,以此类推。

如果节点 X 存储在下标 i 的位置,下标 2*i 的位置就是左子节点,下标 2 * i + 1 的位置就是右子节点。

如果是完全二叉树,仅仅浪费一个下标为0的存储位置。如果是非完全二叉树就会浪费更多的存储空间。

二叉树的遍历

  • 前序遍历:先打印该节点,再打印它的左子树,最后打印它的右子树。
  • 中序遍历:先打印它的左子树,再打印它本身,最后打印它的右子树。
  • 后序遍历:先打印它的左子树,再打印它的右子树,最后打印这个节点本身。

二叉查找树

别名: 二叉排序树(中序遍历)

支持动态数据集合的快速插入、删除、查找操作,为快速查找而诞生的数据结构。

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

二叉查找树的查找操作

首先取根节点,如果等于查找的数据,就返回。如果查找的数据比根节点的值小,那么就在左子树中递归查找;如果查找的数据比根节点大,就在右子树中递归查找。

二叉查找树的插入操作

新插入的数据一般都是在叶子节点上,所以依旧是从根节点开始,依次比较要插入的数据和节点的大小关系。

如果要插入的数据比节点的数据大,并且节点的右子树为空,就将数据直接插入到右子节点的位置;如果不为空就,递归遍历右子树,查找插入位置。同理,如果要插入的数据比节点数小,并且左子树为空,就将新数据插入到左子节点的位置;不为空就再遍历左子树,查找插入位置。

二叉查找树的删除操作

查找和插入操作都比较简单,但是删除操作就比较复杂。

针对删除节点的子节点的个数不同,需要三种情况来处理。

删除的节点没有子节点

直接将父节点中,指向要删除节点的指针设置为 null。

删除的节点有一个子节点

删除的节点只有一个子节点,我们需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点。

删除的节点有两个子节点

要删除的节点有两个子节点;

  • 需要找到改节点的右子树中最小的节点,把它替换到要删除的节点上。
  • 然后删除掉这个最小的节点,因为最小的节点肯定没有左节点(否则就不是最小节点)。

特殊方法: 只是将要删除的节点标记为 已删除 ,并不真正的将该节点删除掉,但是这种比较浪费内存空间,但是删除操作就变得很简单。

二叉查找树的其他操作

  • 支持快速查找最大节点和最小节点,前驱节点和后继及诶单。
  • 中序列遍历二叉查找树: 可以输出有序的数据序列,时间复杂度为O(n)。

支持重复数据的二叉查找树

  • 每一个节点不仅会存储一个数据,通过链表和支持动态扩容的数组等数据结构,将值相同的数据存储在同一个节点上。
  • 每个节点依旧只存一个数据,但是在查找插入的过程中,如果碰到一个节点的值,与插入数据的值相同,就将这个要插入的数据放到这个节点的右子树,也就是把这个新插入的数据当做大于这个节点值来处理。
    • 查找数据: 遇到相同的值并不停止查找操作,而是继续在右子树查找,直到遇到叶子节点,才停止。这样就可以将键值相等的所有节点查找出来。
    • 删除数据:需要找到每个删除的节点,然后按照前面的删除方法来操作。

二叉查找树时间分析

时间复杂度与树的高度成正比,也就是O(height)

二叉查找树: 插入、查找、删除,理想时间复杂度为 O ( l o n 2 n ) O(lon2 n) O(lon2n)

极度不平衡的二叉查找树,当退化成单链表时,时间复杂度就为O(n)

平衡二叉查找树: 树的高度接近 logn,所以插入、查找、删除的时间复杂度为O(logn)

问题

散列表在插入、删除和查找中时间复杂度能做到常量级O(1),为什么还要用二叉查找树呢?

  • 散列表数据是无序的,当有序输出需要先进行排序,但是二叉查找树来说直接进行中序排序时间为O(n)

  • 散列表扩容耗时,当遇到散列冲突时,性能不稳定;但是在工程中用到的平衡二叉树,时间复杂度O(logn)

  • 散列表的时间复杂度是常量级的,但是应为哈希冲突存在,常量不一定比O(logn)小,所以实际速度不一定比O(logn)快,加上哈希函数耗时,也不一定比平衡二叉树效率高。

  • 散列表构造比二叉树复杂,要考虑散列函数的设计、冲突、解决办法、扩容、缩容等。平衡二叉树只考虑平衡性这个问题(解决方案比较成熟)。

你可能感兴趣的:(数据结构)