红黑树node
-
性质
node.color == (red | black)
root.color == black
node.color == red -> node.lchild.color == node.rchild.color == black and node.parent.color == black
NULL == black
length_to_nulls(node).same() == true
-
结构体成员
-
parent
python -
color
函数 -
lchild
ui -
rchild
3d -
key
codeclass rbtree: def __init__(self,key): self.parent = None self.color = red self.lchild = self.rchild = None self.key = key
-
-
名词
-
**黑高: **blog
-
是棵红黑树: 知足上面五个性质。继承
-
bh(node) == node_to_null_with_n_black_node(node)
ip-
是棵红黑树it
-
标记的是向下遍历直到有几个
NULL
。blackhigh = 0 node = node.lchild while node != None: if node.color == BLACK: blackhigh += 1 node = node.lchild
-
-
-
-
旋转
-
right_rotate
def right_rotate(T,y): x = y.lchild y.lchild = x.rchild if x.rchild != None: x.rchild.parent = y x.p = y.p if x.p == None: T.root = x elif y == y.parent.lchild: y.parent.lchild = x else: y.parent.rchild = x x.rchild = y y.p = x
-
-
lchlid_rotate
def lchlid_rotate(T,x): y = x.rchild # 先将y存储到变量中 x.rchild = y.lchlid # 进行重链接操做,将y.lchlid过继给刚失去右孩子的 x . if y.lchlid != None: # 若是y.lchlid不为空,就告诉 y.lchlid 它的新的父亲是谁 y.lchlid.p = x y.p = x.p # 执行 y 由于上位了,也许要知道本身的父亲(上司更好)是谁 if x.p == None: # 也就是 原来的 x 没有父亲,那就说明是根节点 T.root = y elif x == x.p.lchlid: # 若是 原来的 x 是左孩子,y 顶替上来也应该是左孩子 x.p.lchlid = y else: # 若是 原来的 x 是右孩子,y 顶替上来也应该是右孩子 x.p.rchild = y y.lchlid = x x.p = y # 而后执行最后的过继操做
-
-
插入
-
new_node
node = rbtree(key) node.lchild = node.rchild = None node.color = red
-
insert
就是替换一个None
def rb_insert(T,z): y = None # 建立一个 x y模型的迭代器,赋予初始值。y能够当作x的父节点 x = T.root # 从 根开始搜索,肯定须要插入的位置 while x != None: y = x if z.key < x.key: x = x.lchlid else: x = x.rchlid # -------------------------------------------------------------------------- # 这个循环用于查找插入位置,直到找到插入位置位置,y记录插入位置。 z.p = y # 找到插入位置了,那么就先告诉 z 他的父亲是谁,这里是 y if y == None: # 若是 y == None ,说明是空树 T.root = z # 那么 z 做为新的根节点 elif z.key < y.key: # 若是key小于,则插入右子树 y.lchlid = z else: # 不然插入左子树 y.rchlid = z # 在上面的while循环中,大小已经肯定了,插入的位置也必定是空,只是没有记录是左子树仍是 # 右子树而已,这里从新判断一下。 z.lchlid = None z.rchlid = None z.color = red # 对新添加的节点进行着色,为何是红色?由于叶子节点`None`必定是黑色。 rb_insert_fixup(T,z) # 最后维护红黑平衡
-
if newnode.parent.color == red
违背了
node.color == red and node.parent.color == black
就须要维护
-
维护
# 大写表示黑 # 小写表示红 # g 表示 grandparent # p 表示 parent # u 表示 uncle # n 表示 new node ''' 1. 新插入节点确定是红,由于这样插入这样的节点不会影响以前的红黑平衡。 2. 插入的节点替换的是以前的黑节点,使用一个红节点+两个黑节点的组合不会影响这一部分的平衡 3. 父辈的关系 父黑: 爷爷红: 叔叔黑: 不作任何操做 自平衡 叔叔红: 不可能 因此这个状况不须要作任何操做 爷爷黑: 叔叔黑: 不作任何操做 自平衡 叔叔红: 自平衡 因此不作任何操做 因此父亲黑不用作任何操做,只考虑父亲红的状况 父红: 爷爷黑: 叔叔红: case 1 父和叔叔变黑 爷爷变红 黑高增长,须要向上回溯维护更上层的平衡 叔叔黑: 本身和父亲位置相同,都是左子树或者都是右子树: 旋转变色 case 3 自平衡,两边的黑高都不变 本身和父亲位置不一样,一左一右: 旋转,改变平衡状态 1. 若是不旋转: 那么本身将经过旋转过继给爷爷。 过继过去致使原爷爷那一边的黑高大于另外一边,并且还没法变色 2. 旋转 case 2 变为 case 3 爷爷红: 不可能 4. 孙子+爷爷+父亲+叔叔 排列组合有 2^4=16种 其中去掉我出现不会打乱平衡的状况剩下一些我出现会打算平衡的状况。即父亲为黑,不会打乱平衡。 由于 爷爷+父亲+叔叔 是平衡的 是因为孙子的出现才打乱这个平衡,因此就须要从新维持这个平衡 若是孙子的出现,致使了不平衡,而后为了维持平衡, 致使了爷爷的颜色发生了变化,也就是 爷爷变红,即爷爷的黑高变大 就须要向上回溯,那么爷爷的身份就变成了孙子,而后继续维持平衡。这个时候就只须要简单的 维持三者的红黑平衡了。 旋转操做都须要考虑新节点的位置 case 1: G g / \ / \ p u --> P U / / n n case 2: G G / \ / \ p U --> n U \ / n p case 3: G P / \ / \ p U --> n g / \ n U ''' def rb_insert_fixup(T,n): while n.p.color == RED: # 一直向上回溯维护,直到平衡 if n.p == n.p.p.left: # 若是 n 的 父节点 是 左节点 u = n.p.p.right # n.p 是 left # u 是 right if u.color == RED: # case 1 只需简单的修改一下颜色 n.p.color = BLACK u.color = BLACK n.p.p.color = RED # 这种着色 致使了 黑高 + 1 n = n.p.p # 因此须要向上继续维护 else: if n == n.p.right: # case 2 ,先坐旋转,若是不旋转,后面的右旋转旋转 # 则会影响两边的黑高平衡,最终致使两边不平衡 n = n.p left_rotate(T,n) # case 3 简单旋转就能够维护平衡 n.p.color = BLACK n.p.p.color = RED right_rotate(t,n.p.p) # 这个时候 n.p.color 为 BLACK 则就能够不用回溯了,中止了循环回溯 else: # 和上面的操做类似,旋转相反,镜像 pass T.root.color = BLACK # 可能回溯到根节点,根节点可能被修改成红,所以每次都改。 # 整个函数就是用于 插入后 致使的红黑性质被破坏,而后经过循环回溯维护平衡。
-
case 1
- 插入红节点
z
发现父节点是红
,叔叔也是红,则变色,变为case 2
。
- 插入红节点
-
-
case 2
- 左右平衡,可是黑高升高,向上回溯。
z
就至关于新插入节点。 - 发现父亲为红叔叔为黑。且和父节点的方向相反。须要旋转。
- 左右平衡,可是黑高升高,向上回溯。
-
case 3
- 因为以前父子为红,旋转和嫁接不会影响黑高平衡。可是父子为红冲突。
- 父子都同方向,此时的父子左右已经平衡。说明须要旋转变色保持平衡。
- 若是不平衡就过继过去,就会形成混乱。
-
case 4:维护成功
- 父子兄弟都红,爷爷黑,则变色就行,影响局部黑高,不影响整体黑高。
- 子红,父红,兄弟黑,则父子和兄弟没法保持红黑互斥,可使用红色转移,使得树更加饱和,即旋转变色。保持红黑性质。
-
-
删除
删除是采用后继来顶替要删除的节点。而后维护节点平衡。
也就是用后继来进行
偷梁换柱
。而后再用后继的
rchild
顶替。
-
关联节点
r
r.child
r.parent
r.bro
-
形成不平衡由
失去r,而后r.child顶岗形成的
与newR,r.parent,r.bro
的不平衡首先原来的节点是红色是不会影响黑高的。后面的不管什么颜色顶替上来都不会影响。
-
只有替换的是黑才会失衡,须要旋转变色平衡。
def rb_transplant(T,u,v): if u.p == T.nil: # 删除的节点是根节点 T.root = v elif u == u.p.left: # 删除的节点是左子树 u.p.left = v # 顶替原来的左子树,升级 else: # 删除的节点是右子树 u.p.right = v # 顶替右子树 v.p = u.p # 告诉顶替的节点他爹是谁 def rb_delete(T,z): y = z y_origional_color = y.color # y_origional_color 是用来记录 z 生前是啥颜色 if z.left == T.nil: x = z.right rb_transplant(T,z,z.right) elif z.right == T.nil: x = z.left rb_transplant(T,z,z.left) # 若是有一个子树是空,只须要下面的节点来顶替就好了 else: y = tree_minimum(z.right) # 获取后继 y_origional_color = y.color # 可能为黑可能为红,黑也就意味着,右子树可能为红 x = y.right if y.p == z: x.p = z else: rb_transplant(T,y,y.right) y.right = z.right y.right.p = y rb_transplant(T,z,y) y.left = z.left y.left.p = y y.color = z.color # 用后继来顶替z的位置,就好像什么都没有发生同样 # 后继的右子树顶替y的位置,这个时候就须要维持平衡了 # 若是原来是黑色的,那么黑高就收到了影响,不知足红黑树定义,就须要维持稳定了。 # 黑高-1了,x表示是新顶上来的 if y_origional_color == BLACK: rb_delete_fixup(T,x) # 若是原来的是个黑的,删掉了则黑高-1,新的节点就必须的是黑色的。 # 若是顶上来的是红色的,直接改一下颜色就能够了,就直接平衡了 # 若是顶上来的是黑色的,这个时候黑高仍是-1了,那么就须要向上回溯,让上层来保持平衡 def rb_delete_fixup(T,x): while x != T.root and x.color == BLACK: if x == x.p.left: # 若是 x 是 左子树 w = x.p.right if w.color == RED: # 并且右子树是红色,那么 x 的父节点就是黑色,并且X也是黑色 w.color = BLACK x.p.color = RED left_rotate(T,x.p) w = x.p.right # 将这种演变成2,3,4的状况,而后统一处理 if w.left.color == BLACK and w.right.color == BLACK: # 由于x是黑色,w是黑色,w.l 和 w.r都是黑色,去除一层黑色,降黑高 w.color = RED x = x.p # 维护三角平衡 else : if w.right.color == BLACK: w.left.color = BLACK w.color = RED right_rotate(T,w) w = x.p.right w.color = x.p.color x.p.color = BLACK w.right.color = BLACK left_rotate(T,x.p) x = T.root else: '''相反''' pass x.color = BLACK
if w.color == RED: # 顶替的是黑色 # x 是黑色 # w 是红色 # x.p 是黑色 # 那么黑高至少为3 # 右边是红色,w的黑高至少为2 # 也就是说 c 必然不是None,确定有数据 # A可能没有数据是None w.color = BLACK x.p.color = RED left_rotate(T,x.p) w = x.p.right # 将 w 也就是兄弟节点变为黑色 # x 的父亲节点变为红色 # 坐旋转 # w 改变 # 这样是将case 1 转换为下面的 case 2 # 通过这样的转换左边的B不平衡,A可能为空,也就是黑高为1 # 右边的C黑高为2 # ABC不平衡,从新处理,转到 case 2
if w.left.color == BLACK and w.right.color == BLACK: # w的两个孩子都是 Black # 若是是由case 1转换过来的,那么 x 就多是空 # x = x.p 则是回溯,这里若是 x 为空是由问题的,须要修改 # 若是不是从case 1转换过来的,也就是替换上来出现的。 # 那么就是说,B左边黑高原先为2,右边黑高也为2,D确定不为空 # 这种就将 w 也就是兄弟节点变为红色,而后网上回溯,若是 b 是红色,则变成黑色, # B变成黑色,w 变成红色,对于原来的w来讲下降了本身的黑高 # 对于b的左边来讲也下降了本身的黑高,可是经过这种 两黑一红,向上聚拢,总体黑高不变。 # 对于B来讲,算上B,黑高平衡。 # 若是B是黑色的,那么就须要向上回溯,又成了新的 case 1,2,3,4 重新回溯 w.color = RED x = x.p
else: # 不知足上面的条件必然有一个不知足黑色 if w.right.color == BLACK: # 若是 w 的 right 是黑色,那么他的 left 就是红色,以下图 # 若是是从 case 1 转换过来,那么此时的 A 多是黑色,C仍是红色,则说明右边的黑高 # 为 2 ,那么也就是说 A 不为空黑高可能为 2,E则有可能为空 # 经过这种变色右旋的方式,左右两边保持了平衡,可是黑高没有发生变化,重新回溯,变为 # case 2 了 # 若是不是从case 1 转换过来的 # A可能为空,左边的x的黑高仍然仍是下降了,没有获得维护,简单的右旋 # 造成新的左右平衡,而后向上回溯。 w.left.color = BLACK w.color = RED right_rotate(T,w) w = x.p.right
# 若是是从case 3 转换过来的 # 则 c 可能为空,并且C是黑色,由于C以前为红,是子节点顶替上来的,红色的子节点 # 必然为黑 # 上面的转换后的图,仍然知足下面4的起始的特性。 # 这种w D继承父节点 B的颜色 # 父节点转换为黑色,左旋,增长黑高。E变为黑色,同时增长黑高。左旋,平衡 # x = T.root 中断 # 若是不是从case3 转过来的 # 也就是说 E 为红,C可能为黑,可能为红。E为红,则C为空则是黑色,C为红则也没有影响 # 反正D确定是平衡的,E必定为红,C可能为黑,可能为红,并且C也是平衡的。 # 那么w为黑,右边黑高平衡,至少为2。 # 因为w立刻就要升级顶替上层的D # 原来的父节点降级,着色为黑色,那么就是说,坐旋转以后,左边升高,行程平衡。 # 由于E确定为红,则不为空,变色为黑,右边也增高,造成平衡。 # x = T.root 终止向上回溯。 w.color = x.p.color x.p.color = BLACK w.right.color = BLACK left_rotate(T,x.p) x = T.root
''' Case 1 - left rotate at parent P S / \ / \ N s --> p Sr / \ / \ Sl Sr N Sl Case 2 - sibling color flip (p could be either color here) (p) (p) / \ / \ N S --> N s / \ / \ Sl Sr Sl Sr Case 3 - right rotate at sibling (p could be either color here) (p) (p) / \ / \ N S --> N Sl / \ \ sl Sr s \ Sr Case 4 - left rotate at parent + color flips (p and sl could be either color here. After rotation, p becomes black, s acquires p's color, and sl keeps its color) (p) (s) / \ / \ N S --> P Sr / \ / \ (sl) sr N (sl) '''