1.1Range Query(范围查询)
2.1高级搜索树
这里的高级搜索树不一定是二叉搜索树(不一定二叉,但还是搜索,搜索的本质是循关键码)
2.2伸展树(splay)
splay的意思是伸展,将手或者是腿叉开的那种感觉。
伸展树将已经访问的节点通过等价变换调整到树根(这样做的好处是再次访问这个数据时可以迅速访问),可以通过逐层旋转把该节点伸展到树根,但是考虑一个退化成列表(左倾)的二叉搜索树,从小到大的访问顺序,总的时间时O(n^2),分摊O(n)。如果是考虑两层,如果是zigzig或者zagzag先以爷爷为中心转,再以父亲为节点转,这样可以让原来的路劲减半(可以证明这样的分摊只要logn时间)。在这个例子中,伸展树还没伸展时,节点的访问可能达到n的时间,逐渐伸展后,分摊时间logn,再考虑可能会重复调用某个节点,这样的时间在平均上可能会更好。(所以伸展一方面可以利用重复访问的特性,一方面在某种程度上降低树的高度,这都是二叉搜索树所需要的)
实现:
splay接口,将节点伸展到根节点。注意到其中的双层伸展,是针对子父爷三代,3+4重构也是针对三代,对比一下可知:当是zigzag(zigzag的描述是针对顺序而言,具体来说是从父zig爷zag,所以从下到上是左右)时,双层伸展的等价变换与逐层伸展以及3+4重构的结果一模一样,当zigzig时,三者均不同,逐层变换是先父zig,再爷zig;双层伸展是爷zig再父zig;3+4仅仅是爷zig一次。
注意,等价的变化变的只是树中的逻辑位置,实际树节点的物理位置一个没变。(节点属性值如高度改变了)
重写查找、删除和插入。查找再查找完成后直接对hot或者查找到的节点进行伸展。伸展树的查找,与常规BST::search()不同:很可能会改变树的拓扑结构,不再属于静态操作
插入:插入的返回值是根节点,无论是否有实质性的插入,该节点值都等于要插入的关键码;
删除:删除的返回值是ture or false。
评价:分摊成本logn,适用于多次查找(时间上的分摊),且访问的数据具有局部性(伸展到根,时间更短)。复杂度的分析可以证明(暂时搁置)
2.3 B-树
首先要考虑的是数据访问的局部性。下个时间段访问的数据很可能是与上次访问的相邻数据,而考虑到这一点,缓存的功能也就是访问数据是按page或块的读取与要访问数据相邻的一堆数据,接下来访问的如果是相邻的直接在缓存里找即可,也就是说如果利用缓存,在算法上利用这种读取的局部性能加快效率。将一个数组向左移动k个单位的例子中,比较两种算法,一种是任选取一个元素,将其后的k个元素替换过来,迭代进行(要GCD(n,k)轮迭代),总的时间复杂度O(n+g),但是这种算法每次访问数据都隔了k个,可能利用不了局部性。另一种算法,用三次数组交换可以实现,大约3n的复杂度,虽然看似比上个算法要慢,但是交换的过程中数据的访问是有局部性而不是跳跃的,在读取的时间上优于上述算法。所以要尽量利用批量访问,减少IO次数(访问缓存不算IO)。
B树考虑到了这一点。假设大量数据以BBST(如AVL)的数据结构存在在外存,那么搜索的过程中每下一层就需要进行一次IO,因为上下层的数据不是储存在连续的内存中。也就是要进行logn次的IO。而B树将连续几层的节点地址存在一段连续的空间中,这样能很好的应用外存的批量访问,一次IO能下降好几层。(以一个节点中有2^8=256个节点为例,B树的一次IO等于普通BST的8次,比较次数相等,但是IO次数明显下降)
B树的节点类不再是binnode,而是BTnode。类中的成员包括父亲节点的位置(与binnode一致),孩子节点位置(与binnode不同,孩子可能有很多个,所以要用向量vector<BTnodeposi<T>>表示),自身关键码数据(与binnode不同,有多个关键码,用vector<T>表示)。构造函数有针对B树根节点的构造,以及无关键码节点构造。
B树类中成员有树根节点位置,关键码数量,hot节点(上述三个和bst一致)以及_m阶次(B树最重要的定义就是分支树在m/2到m之间)。接口上有循关键码的查找、插入和删除以及处理上溢和下溢的函数。
查找:对节点关键码向量进行查找,无就查找子树,直到子树为空。
插入:插入后可能发生上溢(即关键码个数超出B树的要求),处理上溢的办法是通过分裂(把溢出的节点分裂成两个)
删除:删除首先要保证删除关键码是在叶子节点中(因为除了删除关键码向量中的关键码,还要删除子孩子向量中对应的子孩子,如果不是叶节点会导致有关键码的子孩子被删除),可以通过关键码互换来实现。删除可能会引起下溢(关键码个数低于B树要求),处理的办法是先检查左右兄弟,如果能旋转解决的话旋转解决,不行再使用合并的方法。
2.4红黑树
红黑树每次删除或者插入操作只会引起常数局部的拓扑结构变化,与avl树不同(这样有利于持久性?这里不太理解)。
红黑树在普通的BST上有四条规则:根节点为黑,外部节点为黑(全部是引入的),红节点只能有黑孩子(这一条能推出红节点也只能有黑父亲),外部节点黑深度相等(深度在bst中的定义是路径的长度,这里的外部节点黑深度也可以理解为外部节点到根节点的黑路径长度,这个长度一定会等于路径上真实黑节点数量,因为外部节点和根节点都是黑色,注意不能根据所有叶子节点的黑深度相等来判定红黑树,因为不一定只有叶子节点才会有外部节点)
注意通过对比红黑树与B树的定义理解红黑树与(2,4)B树的等价性:红黑树中的外部节点黑深度相等对应于B树中的所有叶子节点的深度相等,且外部节点只会出现在最底层,所以一定严格满足孩子比关键码多一(因为是加上外部节点是真二叉树)。红黑树的高度小于黑高度的两倍等于B树高度,因为B树高度是平衡的,所以红黑树高度也是平衡的。红黑树中黑节点数量等于对应B树中超级节点数目(有两个红孩子的黑节点对应于三个关键码节点,有一个红孩子节点对应于二个关键码节点,没有红孩子对应于一个关键码节点)
红黑树节点(子树)高度:普通bst叶子节点高度为0,外部节点高度-1;红黑树外部节点高度0,高度为黑高度(是根据黑节点数量计算)(等价的B树高度比黑高度小1)
插入:插入的节点颜色只能是红色(如果是黑色会导致外部节点黑深度不相等),所以可能会出现双红出现(违背了红节点只能有黑孩子)。如何解决双红:根据叔父节点颜色进行区分,在处理的过程中注意与之等价的B树的变化。
删除:removeAt( x, _hot )的用途得以体现,不仅在普通BST中可以使用,在红黑树重写删除时也可以使用。删除的双黑是指删除之前双黑(与插入不同,插入是插入之后双红)。可以分四种情况进行处理,有意思的一种是删除节的父亲和兄弟都为黑,且兄弟的孩子也为黑,这样经过染色后可以等效于再往上一层被删除了。