B树:
可以看成多路平衡搜索树,但是,可以把平衡二叉搜索树压缩合并成多路平衡搜索树,比如两层两层合并,那么一个节点就有3个关键字,4个孩子了,还可以3层合并、4层合并...既然有了平衡二叉搜索树,为什么还要有B树呢?好像是因为转换不方便。
用B树的动机在于减少I/O操作。这里假设B树存的信息量很大,无法存在内存中,都存在外存来说。比如一个查找操作,需要读取logkN次的节点关键字信息,每次根据k个关键字的信息找到在哪个分支。这里前一个步骤需要I/O操作,后一个步骤是在内存中操作,因此k取较大值就好,一般在200-500范围内。
实现了一下,用POJ 3481 http://poj.org/problem?id=3481 这题来测试,代码地址:http://codepad.org/oVrBk7oc
竟然要将近300行,可能写得比较挫。主要是插入和删除,插入要插入到叶子中,然后从叶子开始调整,若当前节点超过M的限制,则分裂成两个,把中间的关键字插入到父节点中,若父节点也超过了M的限制,则继续往上调整。删除也是要从叶子开始,若低于M/2,则从兄弟节点移一个过来,若兄弟节点刚好是下限移不了,则可以跟兄弟节点合并,把父亲节点的关键字拿下来一起合并,相当于在父亲节点中删除了那个关键字,就这样不断往上删除调整。
B+树:
和B树区别在于,内部节点只存用于查找的关键字,具体的关键字和与之对应的信息存在叶子节点中,同时每个叶子节点有个连向后一个叶子节点的指针,因此可以方便地进行遍历。还有叶子节点的关键字个数上下限可以和内部节点不同,内部节点的上下限比较大,因为内部节点只要存关键字。
还是拿POJ那题测试,蛋碎了都。OOP能力实在是渣。颓废了调试了2天,都想放弃了,晚上喝了酒玩了一晚上游戏,一时兴起再看看代码,发现了好几个BUG,改过之后AC了。CTMD的,真爽。代码:http://codepad.org/Q4m234Zd 。 哦,叶子节点的next指针可能有问题,之前调试时发现出过问题,干脆不用那个next了,后面没再测试过。以后再看吧,不想弄了。
红黑树:
看了清华大学的数据结构公开课才知道,红黑树的优势原来是在可持久化上。之前写过可持久化的线段树、无旋转treap ,一个操作后的结构变化量都是O(logn)级别的,而红黑树的结构变化量是O(1)的,虽然操作复杂度都是O(logn)。不过,颜色变化量是O(logn)的,好像还是要记录颜色变化啊...好吧,红黑树的优势估计就是常数比较小了- -。
红黑树优势应该就是常数小,插入删除操作旋转数都是O(1),剩下就是染色。另外存的额外信息就只有颜色,只需要一位记录,看到有人说可以直接在指针上的某一位上记录。
红黑树实质就是4阶B树。将红色节点提升到其父节点,这样就是一颗B树了。红黑树的调整都可以从B树的角度理解。细节挺多的,以后有空再实现...
skiplist(跳表):
知乎上看到有人说算法导论第四版可能将红黑树去掉,换成skiplist...去看了并且实现了下,概念简单,实现也容易,就是效率不太行,POJ这题B树300多ms,skiplist要500多ms,红黑树我估计应该是在100ms以内的。据说Redis的源码里有用到skiplist,以后有空看看。
skiplist实质就是将数据分层,每一层用链表按key有序地存下数据,其实上层可以只存key不存具体数据,最底层包含所有数据。越上层存的节点越少,按照随机,每个节点在上一层出现的概率是1/2,每个节点在某一层出现的前提是在下一层中有出现。然后每个节点除了保存当前层后一个节点的指针外还要保存指向下一层同一节点的指针。插入、删除、查找都是类似的,以查找来说,就是上层开始找,上层找不到再跳到下一层。复杂度期望是O(logn)的。还是用POJ 3481测试: http://codepad.org/Fi5KyMAp