二叉查找树:左子节点小于根节点,右子节点大于根节点
跑道预留系统(Runway Reservation System)
让我们先从机场跑道预留系统作为引子,引入二叉查找树的概念。
问题定义
假设有一个机场只有一条跑道预留给飞机降落,这个系统有以下约束:
预留的需求决定了降落时间$t$,$t$必须是未来的时间
如果在$k$分钟内没有规划其他的降落请求,那么就将$t$加入集合$R$中。
飞机降落后,将$t$从$R$中移出
对于规模为$n$的$R$,我们希望上述操作时间复杂度为$O(\lg n)$
例子
假设降落规划的时间轴如上图,现在有三个规划请求,分别是在53、44和20秒降落,根据约束可知44和20是无效的请求。现在我们需要一个合适的数据结构描述规划。可选的数据结构包括:
- 未排序的链表或数组:不符合时间复杂度,常规操作基本都是线性复杂度
- 排序的数组:查找符合$O(\lg n)$的复杂度,但是插入的操作需要线性复杂度,因为需要移动
- 排序的链表:链表做二分查找有难度
- 堆:查找需要线性的时间,因为堆只提供了最大(最小)元素的$O(1)$时间查找
从上面的数据结构可以看出,它们或多或少都有一些问题,所以我们需要一个新的结构解决上述问题,即我们今天要介绍的BST。
二叉查找树(BST)
树的组成
一个简单的二叉查找树如下:
1 | 5 |
树的组成为:
- 节点$x$:key($x$)为该节点的值
- 指针:parent($x$),left($x$),right($x$),parent指向节点$x$的父节点,left指向左子节点,right指向右子节点
性质
二叉查找树的最重要的性质为:对于所有节点$x$,$x$左子树的节点值小于等于$x$的值;$x$右子树的节点值大于等于$x$的值。根据这个特性,BST的中序遍历是一个有序数列
操作
插入删除
插入删除的时间复杂度取决于树的高度$h$,为$O(h)$。
寻找最小值find_min()
寻找最小值就是一路向左,寻找最大值就是一路向右,所以时间复杂度为$O(h)$。
寻找下一个较大的值next_larger(x)
时间复杂度也是$O(h)$
寻找比某个节点小的所有节点的个数rank(x)2
时间复杂度也是$O(h)$,这个过程分为三步:
- 第一步寻找插入点
- 第二步寻找过程中加上比该节点小的节点的个数
- 第三步加入该节点的左子树的所有节点
树的增长与收缩
当插入或删除节点时,树会动态地调整大小,而我们也需要对树进行调整,从而使得树始终满足二叉搜索树的性质。我们有一个简单的二叉树如下:
其中节点右侧的数表示以该节点为根节点的树的规模。现在,我们要往树中插入新的节点43,我们需要从49开始,依次向下遍历,每经过一个节点,就将节点的规模+1,得到的新树如下所示:
BST实现
下面是一个使用java实现的BST:
1 | /****************************************************************************** |