平衡二叉树系列(1)——红黑树及其插入
1970 年,31 岁的 Hopcroft 教授研究出了一种新的数据结构,叫 2-3 树。后来的 B 树就是以它为基础的。1972年,Bayer 在 B 树的基础上又发明了“对称二叉 B 树”。六年后,施乐帕洛奥图研究中心(这个地方也是 GUI、Smalltalk 和鼠标的诞生地。)的 Guibas 和 Sedgewick 发了一篇论文,叫“平衡树的双色框架”。众所周知,框架是用来套的。把这个框架套给 Bayer 口中所说的“对称二叉 B 树” ,也就是下文将要提到的 2-3-4 树,之时,红黑树就诞生了。
从朴素的 2-3-4 树聊起……
要更好地理解红黑树中奇奇怪怪的性质,我们还要从最初的 2-3-4 树聊起(为什么不是 2-3 树呢?因为 2-3 树中不允许 4 节点存在,因此插入新数据时要把节点拆来拆去,不如 2-3-4 树方便。)。这种树有三类节点:2 节点、3 节点和 4 节点。2 节点可以存下一个数据,最多连两个子节点,和普通的二叉搜索树一样;3 节点可以存下两个数据,最多连三个子节点;4 节点可以存下三个数据,最多连四个子节点。
那么,插入数据时的规则又是什么呢?看下面的图片。给 2、3 节点插入数据,2、3 节点会分别变成 3、4 节点。
给 4 节点插入数据就有些不一样了:这时我们先把 4 节点中间那个数提起来,之后再像之前一样插入新节点。
现在,我们用 1, 9, 2, 8, 3, 7, 4, 6, 5 这组数据(这组数据是从 Guibas 和 Sedgewick 的论文里借来的。)搭一棵 2-3-4 树,切身感受一下 2-3-4 树是怎样在插入数据的过程中保持自身平衡的。
首先是前三个数据的插入。当然这很简单,不需要过多的解释——
之后我们要插入 8 了。应该怎么办呢?
之后我们插入 3 和 7 两个数——过程和前面基本相似。
最后就是 4、5、6 三个数了。
你明白了么?
双色 2-3-4 树
2-3-4 树怎么实现呢?
大家可能想到了面向对象——没错,继承和多态可以帮助我们很方便地实现像“在一棵树里存下三种不同节点”之类的需求。但是,在这些数据结构刚刚出现的年代,“面向对象”的概念还没有流行开来;当时的 C 语言也只是刚刚出世还没多久的新语言。那该怎么办呢?
大家的第一反应可能是用 C 语言来模拟面向对象。但是呢,这样做不仅麻烦,也不太方便理解和维护。当然大家也可能想到了直接用 4 节点来表示所有节点,但是这样也不太好——费空间呀。
所以我们用另一条途径解决这个问题—— 2 节点,好办;3 节点就用两个连在一起的 2 节点表示;4 节点么,用三个 2 节点表示。至于区分的问题,对于一般的两个连在一起的 2 节点,我们用黑线连接;表示 3 节点或 4 节点的的 2 节点,我们用红线连接(此之谓“双色”)。这样不就容易实现了吗?也就是说,3 节点、4 节点和 2 节点们的对应关系是这样的: