vlambda博客
学习文章列表

【红黑树】自平衡二叉查找树

红黑树是一种自平衡二叉查找树。它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI—STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 可以在O(logn)时间内做查找,插入和删除等操作


~基本性质

红黑树,通过红黑两种颜色域保证树的高度近似平衡。它的每个Node节点是一个五元组

color(颜色),key(数据),left(左孩子),right(右孩子)和 p(父节点)

红黑树的定义也是它的性质,有以下五条:

  • 性质1:节点是红色 or 黑色

  • 性质2:根是黑色

  • 性质3:所有叶子都是黑色(叶子是NIL节点)

  • 性质4:如果一个节点是红的,则它的两个儿子都是黑的

  • 性质5:任一节点到其叶子的所有简单路径都包含相同数目的黑色节点。

这五个性质强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长~为什么呢?

性质4暗示着任何一个简单路径上不能有两个毗连的红色节点,这样,最短的可能路径全是黑色节点,最长的可能路径有交替的红色和黑色节点。同时根据性质5知道:所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。


~基本操作

因为红黑树也是二叉查找树,因此红黑树上的查找操作与普通二叉查找树上的查找操作相同。然而,红黑树上的插入操作和删除操作会导致不再符合红黑树的性质。恢复红黑树的性质需要少量(O(log n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为 O(log n)


~插入操作

插入操作可以概括为以下几个步骤:

  1. 查找要插入的位置,时间复杂度为O(N)(跟普通二叉查找树一样)。

  2. 将新节点的color赋值为红色

  3. 自下而上重新调整该树为红黑树


⚠️~为什么第(2)步之所以将新插入的节点的颜色赋为红色?

若设为黑色节点,就会导致根到叶子的路径上有一条路径上,多一个额外的黑节点,这个是很难调整的。

若设为红色节点,可能会导致出现两个连续红色节点的冲突,则可以通过颜色调换(color flips)和树旋转来调整,实现起来简单。

【场景如下】

设要插入的节点为N,其父节点为P,其父亲G的兄弟节点为U(即P和U是同一个节点的两个子节点)。

  • 如果P是黑色的,则整棵树不必调整便是红黑树。

  • 如果P是红色的(可知其父节点G一定是黑色的),则插入N后,违背了性质4,需要进行调整。

调整时分以下3种情况:

【红黑树】自平衡二叉查找树

如上图所示,将P和U重绘为黑色并重绘节点G为红色(用来保持性质5)。现在新节点N有了一个黑色的父节点P,因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点G的父节点也有可能是红色的,这就违反了性质4。为了解决这个问题,在祖父节点G上递归调整颜色。


【红黑树】自平衡二叉查找树

如上图所示,对P进行一次左旋转调换新节点和其父节点的角色; 接着,按情形(c)处理以前的父节点P以解决仍然失效的性质4。


【红黑树】自平衡二叉查找树

如上图所示,对祖父节点G 的一次右旋转; 在旋转产生的树中,以前的父节点P现在是新节点N和以前的祖父节点G的父节点,然后交换以前的父节点P和祖父节点G的颜色,结果的树满足性质4,同时性质5[4]也仍然保持满足。


~删除操作

删除操作可以概括为以下几个步骤:

  1. 查找要删除位置,时间复杂度为O(N)跟普通二叉查找树一样)。

  2. 用删除节点后继节点替换该节点(只进行数据替换即可,不必调整指针,后继节点是中序遍历中紧挨着该节点的节点,即:右孩子的最左孩子节点)

  3. 如果删除节点的替换节点为黑色,则需重新调整该树为红黑树

第2步之所以用后继节点替换删除节点,是因为这样可以保证该后继节点之上仍是一个红黑树,而后继节点可能是一个叶节点或者只有右子树的节点,这样只需用有节点替换后继节点即可达到删除的目的。如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题。可参考:http://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91

第3步中,如果删除节点为红色节点,则他的父亲和孩子全为黑节点,这样直接删除该节点即可,不必进行任何调整。如果删除节点是黑节点,分4种情况:

设要删除的节点为N,其父节点为P,其兄弟节点为S。由于N是黑色的,则P可能是黑色的,也可能是红色的,S也可能是黑色的或者红色的。

(1)S是红色的

【红黑树】自平衡二叉查找树此时P肯定是红色的。对N的父节点进行左旋转,然后把红色兄弟转换成N的祖父。接着对调N的父亲和祖父的颜色。尽管所有的路径仍然有相同数目的黑色节点,现在 N 有了一个黑色的兄弟和一个红色的父亲,所以可以接下去按 (2)、(3)或(4)情况来处理。

(2)S和S的孩子全是黑色的

【红黑树】自平衡二叉查找树

在这种情况下,P可能是黑色的或者红色的,简单的重绘S为红色。结果是通过S的所有路径,它们就是以前不通过N的那些路径,都少了一个黑色节点。因为删除N的初始的父亲使通过N的所有路径少了一个黑色节点,这使事情都平衡了起来。但是,通过P的所有路径现在比不通过P的路径少了一个黑色节点。接下来,要调整以P作为N递归调整树。

(3)S是黑色的,S的左孩子是红色,右孩子是黑色

这种情况下在S上做右旋转,这样S的左儿子成为S的父亲和N的新兄弟。接着交换S和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在N有了一个右儿子是红色的黑色兄弟,所以进入了情况(4)。N和它的父亲都不受这个变换的影响。

(4)S是黑色的,S的右孩子是红色


在这种情况下在N的父亲上做左旋转,这样S成为N的父亲和S的右儿子的父亲。接着交换N的父亲和S的颜色,并使S的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以属性3没有被违反。但是,N现在增加了一个黑色祖先: 要么N的父亲变成黑色,要么它是黑色而S被增加为一个黑色祖父。所以,通过N的路径都增加了一个黑色节点。