问题
跑道预订系统。一个飞机场只有一个飞机跑道,需要为未来的飞机着陆预留空闲的跑道。飞机预订的着陆时间为t,假如没有其他的计划在(t-k,t+k)的时间内着陆的飞机,则将t加入集合R。其中k是变量。请问有没有一种时间复杂度为O(logn)的算法解决这个问题?
例子
如果现在时间是37, 在R集合中,在41.2,49,和56.3三个时间点上,其他飞机已经预订了跑道。如果有架飞机要预订t=53进行着陆,是可以的,因为49+3<53<56.3-3;而如果是t=44,飞机无法安排因为t=41.2的飞机着陆完毕后t=41.2+3=44.2>41.2。而t=20的时间也不能安排,因为现在已经是t=37了。
如果集合R是未排序的数组或链表;则插入需花费O(1)的时间,但插入前需要依次和集合中的所有元素进行比较,花费的时间是O(n)。
如果时间集合R是个已排序的数组或链表,我们可以尝试用二分查找来做,查找的时间复杂度为O(logn),但如果是数组,则插入时需要移动大量元素,时间复杂度为O(n),总时间复杂度为O(logn+n)。如果是单纯的链表,则无法使用二分查找法。
以上几种方式都无法满足O(logn)的需求。
二叉搜索树
binary search tree(BST)
二分搜索树的不变式,对于所有结点x:
- 如果y是x的左子树,y的值 ≤ x的值;
- 如果y是x的右子树,y的值 ≥ x的值;
例子在图中。
若h为BST的高度,那么插入元素(不带检查)的时间复杂度是Ο(h)。
最好的情况是BST为平衡的,h=logn,时间复杂度为O(logn)。最差的情况是BST变成一条链表,h=n,时间复杂度为O(n)。
显然BST还是无法完全满足要求。为什么呢?因为BST不平衡。
为此提出了平衡二叉树AVL。
AVL
我们称BST是平衡的,如果其高度为logn。
AVL树的性质:每一个结点的左右孩子结点的高度差距最多为±1。即
AVL树是平衡的。
结点的高度
结点的高度等于该结点到以其作为根节点的子树的叶子结点的最长路径。
等于max{height(left child),height(right child)}+1
AVL树的最少结点数与最大高度
AVL树是平衡的。最差的情况是每一个结点的左右孩子结点的高度差为1.
记Nh=高度为h的AVL树的最少结点数量,并且根据AVL的递归性质可得:
由Nh的递归式我们可以估算AVL树的最大高度。
这个数列与斐波那契数列相似。会得到Nh > Fh = (φ^h)/√5,其中Fh是斐波那契数列(数字1,1,2,3,5,8-构成了一个序列。这个数列前面相邻两项之和,构成了后一项),φ=1.618,最后可以通过下图的得到h小于1.44log2n。
一个更粗略的估算是,因此h < 2*logn
AVL树的插入
分为两步
①根据BST的规则进行插入
②对插入后的BST进行重新平衡,使其符合AVL树的性质。
左旋与右旋
进行左旋或右旋操作后,从左往右的次序保持不变。
例子
往已有的AVL树中插入23,不符合AVL性质,右旋29,符合AVL性质。
插入55,不符合AVL性质,左旋50,还是不符合AVL性质,再右旋65,最终符合AVL。进行了两次旋转。
平衡因子
在二叉树中,节点 X 的平衡因子被定义为高度差
重新平衡
详细讨论AVL插入的步骤②
- 假设x是最低的违背AVL性质的结点(其平衡因子为+2或-2)
- 假定z是拥有更高子树的孩子
有两种情况
①单次旋转
②两次旋转
AVL排序
1.按AVL的规则,插入n个结点。O(nlogn)
2.中序遍历AVL树。O(n)
故AVL排序的时间复杂度为O(nlogn)
ADT
一个抽象数据类型规定了 插入/删除、返回最小值、返回前驱/后继 三种操作。
使用Heap可以实现插入/删除和返回最小值。但是如果还要求next_larger和next_smaller(前驱和后继),则需要平衡的BST。