红黑树:个人理解与Python实现

【基本事实1】

红黑树是一种平衡的二叉查找树,无论插入还是删除操作都可以在O(lg n)内实现,而一般的二叉查找树则在极端情况下会退化为线性结构。
红黑树之所以是平衡的二叉查找树,是因为每个节点都有表示其颜色的域值:红或黑,在插入和删除操作的时候依据节点的颜色向平衡的方向调整。根本原因当然是由红黑树定义所决定的:
如果一个二叉查找树满足如下条件,那么它就称作红黑树:
1.每个节点要么是红色,要么是黑色
2.根结点是黑色
3.每个叶节点(NIL)为黑色
4.如果一个节点是红色,其儿子节点一定是黑色
5.对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点

【个人理解1】

红黑树的定义中,我认为有这些地方要注意:
1.每个节点只有一种颜色(后面删除节点的操作时引入的“额外一重黑色”则不满足此条件,所以一直要性质1调整)
2.定义中的叶节点是指NIL,它们都是黑色,而且没有子节点。根结点的父节点也是NIL。比如只有一个根节点的红黑树,它有两个叶节点。NIL是没有值的,只是一种存在。(我在Python的实现中,把NIL值都设定为None)
3.如果一个节点是红色的,它一定不是根结点,而且一定有父节点(父节点也一定是黑色的);如果它有儿子节点则一定是黑色儿子节点(每个节点如果左右儿子都是NIL,我认为它没有儿子)
4.性质5说的就是黑高度了

【基本事实2】

红黑树的旋转
红黑树在INSERT和DELETE的过程中,会使用到旋转操作。红黑树有两种旋转:左旋和右旋

左旋x:从右图到左图的过程
右旋y:从左图到右图的过程

【个人理解2】

1.并非每个节点在INSERT或DELETE的过程中都需要旋转操作
2.左旋就是右儿子y取代父节点x,x作为y的做儿子,y原来的左儿子b成为x现在的右儿子
3.右旋就是左旋的逆向过程

【基本事实3】

红黑树的插入
INSERT一个值的过程,就是在二叉查找树的INSERT操作基础上,根据红黑树性质做必要的调整,比如颜色变化,比如旋转,也就是INSERT_FIXUP的过程
插入的节点一般设定为红色然后再调整

【个人理解3】

假设要插入的节点为z,INSERT操作就是把z放到树的最底层,然后用INSERT_FIXUP区调整。INSERT_FIXUP中要注意的是:
1.如果z的父节点是NIL(即:插入节点z之前红黑树为空),那么把z涂黑,并成为根结点(完成插入)
2.如果z的父节点是黑色的,不用调整(完成插入)
3.如果z的父节点是红色的:
3-0:如果z没有叔叔节点,那么:
3-0-0:如果z为右儿子,且z的父节点p为左儿子,则左旋z的父节点,成为3-0-1;如果z为左儿子,且z的父节点p为右儿子,则右旋z的父节点p,成为3-0-1;
3-0-1:如果z为左儿子,则“父涂黑,爷涂红”,然后如果父节点是左儿子,则“爷右旋”,否则“爷左旋”(完成插入)
3-1:如果z的叔叔节点为黑色,那么:
3-1-0:如果z是右儿子,且z的父节点p为左儿子,则左旋z的父节点,成为3-1-1;如果z为左儿子,且z的父节点p为右儿子,则右旋z的父节点p,成为3-1-1;
3-1-1:如果z是左儿子,那么“父涂黑,爷涂红”,然后如果父节点是左儿子,则“爷右旋”,否则“爷左旋”(完成插入)
3-2:如果z的叔叔节点为红色,那么“父涂黑,叔涂黑,爷涂红”,并对爷爷节点g调用INSERT_FIXUP过程
以上序号和《算法导论》中的对应关系:3-2对应case1,3-1-0对应case2,3-1-1对应case3

【基本事实4】

1.红黑树删除一个节点的操作比较复杂,但也是在二叉查查找树的基础上,调用DELETE_FIXUP过程来调整
2.如果要删除的节点z,它有两个儿子,那么让z的值设定为z的后继节点y的值,然后删除y
3.如果要删除的节点z只有一个儿子x,那就让z的节点p和x成为“父子”。如果z原来是红色的,则不必调用DELETE_FIXUP过程,否则要调用
4.如果要删除的节点z没有儿子:那就直接删除z好了
5.删除节点时引入了“额外的一层黑色”,《算法导论》中文第二版P173这样说:
“在RB—DELETE中,如果被删除的节点y是黑色的,则会产生三个问题。首先,如果y原来是根结点,而y的一个红色的孩子成为了新的根,这就问犯了性质2)。其次,如果x和p[y](现在也是p[x])都是红的,就违反了性质4)。第三,删除y将导致先前包含y的任何路径上黑节点个数少1。因此,性质5)被y的一个祖先破坏了。不久者恶问题的一个办法就是把结点x视为还有额外的一重黑色。也就是说,如果将人以包含结点x的路径上黑节点个数加1,则在这种假设下,性质5)成立。当将黑节点y删除时,将其黑色“下推”至其子节点。现在为题变为结点x可能既不是红,又不是黑,从而违反了性质1)。结点x是双重黑色或红黑,这就分别给包含x的路径上黑结点个数贡献2个或1个。x的color属性仍然是RED(如果x是红黑的)或BLACK(如果x是双重黑色)。换言之,一个结点额外的黑色反映在x指向它,而不是它的color属性。”

【个人理解4】

1.当你想举反例推翻某个“结论”时,请注意,你的反例中的红黑树可能并不是红黑树;或者,它满足红黑树的定义,但无法判断是否能通过“每次插入一个节点”的方式生成。
2.因为有“额外一重黑色”的存在,《算法导论》中关于红黑树删除的case1中,调整前后的两幅图虽然“看上去是镜面对称”,但前者不满足性质5,调整后满足性质5
3.《算法导论》中关于红黑树删除的case2中,x现在为黑色,且有额外的一重黑色(就像是背负着子孙们的希望。。。),此时将x和w都去掉一个黑色,然后使p(x)增加额外的一层黑色。由于w原本为黑色,则现在令w为红色即可。此时令new_x=p(x),若new_x原本为红色,置黑即可结束;否则,对new_x调用DELETE_FIXUP过程
4.DELETE_FIXUP过程,调整的是DELETE(z)过程中z的左/右儿子(当z只有一个儿子时),或者z的后继的右儿子

#coding:utf8
#author:HaxtraZ
#description:红黑树,python实现
from random import randint RED = 'red'
BLACK = 'black' class RBT:
def __init__(self):
# self.items = []
self.root = None
self.zlist = [] def LEFT_ROTATE(self, x):
# x是一个RBTnode
y = x.right
if y is None:
# 右节点为空,不旋转
return
else:
beta = y.left
x.right = beta
if beta is not None:
beta.parent = x p = x.parent
y.parent = p
if p is None:
# x原来是root
self.root = y
elif x == p.left:
p.left = y
else:
p.right = y
y.left = x
x.parent = y def RIGHT_ROTATE(self, y):
# y是一个节点
x = y.left
if x is None:
# 右节点为空,不旋转
return
else:
beta = x.right
y.left = beta
if beta is not None:
beta.parent = y p = y.parent
x.parent = p
if p is None:
# y原来是root
self.root = x
elif y == p.left:
p.left = x
else:
p.right = x
x.right = y
y.parent = x def INSERT(self, val): z = RBTnode(val)
y = None
x = self.root
while x is not None:
y = x
if z.val < x.val:
x = x.left
else:
x = x.right z.PAINT(RED)
z.parent = y if y is None:
# 插入z之前为空的RBT
self.root = z
self.INSERT_FIXUP(z)
return if z.val < y.val:
y.left = z
else:
y.right = z if y.color == RED:
# z的父节点y为红色,需要fixup。
# 如果z的父节点y为黑色,则不用调整
self.INSERT_FIXUP(z) else:
return def INSERT_FIXUP(self, z):
# case 1:z为root节点
if z.parent is None:
z.PAINT(BLACK)
self.root = z
return # case 2:z的父节点为黑色
if z.parent.color == BLACK:
# 包括了z处于第二层的情况
# 这里感觉不必要啊。。似乎z.parent为黑色则不会进入fixup阶段
return # 下面的几种情况,都是z.parent.color == RED:
# 节点y为z的uncle
p = z.parent
g = p.parent # g为x的grandpa
if g is None:
return
# return 这里不能return的。。。
if g.right == p:
y = g.left
else:
y = g.right # case 3-0:z没有叔叔。即:y为NIL节点
# 注意,此时z的父节点一定是RED
if y == None:
if z == p.right and p == p.parent.left:
# 3-0-0:z为右儿子,且p为左儿子,则把p左旋
# 转化为3-0-1或3-0-2的情况
self.LEFT_ROTATE(p)
p, z = z, p
g = p.parent
elif z == p.left and p == p.parent.right:
self.RIGHT_ROTATE(p)
p, z = z, p g.PAINT(RED)
p.PAINT(BLACK)
if p == g.left:
# 3-0-1:p为g的左儿子
self.RIGHT_ROTATE(g)
else:
# 3-0-2:p为g的右儿子
self.LEFT_ROTATE(g) return # case 3-1:z有黑叔
elif y.color == BLACK:
if p.right == z and p.parent.left == p:
# 3-1-0:z为右儿子,且p为左儿子,则左旋p
# 转化为3-1-1或3-1-2
self.LEFT_ROTATE(p)
p, z = z, p
elif p.left == z and p.parent.right == p:
self.RIGHT_ROTATE(p)
p, z = z, p p = z.parent
g = p.parent p.PAINT(BLACK)
g.PAINT(RED)
if p == g.left:
# 3-1-1:p为g的左儿子,则右旋g
self.RIGHT_ROTATE(g)
else:
# 3-1-2:p为g的右儿子,则左旋g
self.LEFT_ROTATE(g) return # case 3-2:z有红叔
# 则涂黑父和叔,涂红爷,g作为新的z,递归调用
else:
y.PAINT(BLACK)
p.PAINT(BLACK)
g.PAINT(RED)
new_z = g
self.INSERT_FIXUP(new_z) def DELETE(self, val):
curNode = self.root
while curNode is not None:
if val < curNode.val:
curNode = curNode.left
elif val > curNode.val:
curNode = curNode.right
else:
# 找到了值为val的元素,正式开始删除 if curNode.left is None and curNode.right is None:
# case1:curNode为叶子节点:直接删除即可
if curNode == self.root:
self.root = None
else:
p = curNode.parent
if curNode == p.left:
p.left = None
else:
p.right = None elif curNode.left is not None and curNode.right is not None:
sucNode = self.SUCCESOR(curNode)
curNode.val, sucNode.val = sucNode.val, curNode.val
self.DELETE(sucNode.val) else:
p = curNode.parent
if curNode.left is None:
x = curNode.right
else:
x = curNode.left
if curNode == p.left:
p.left = x
else:
p.right = x
x.parent = p
if curNode.color == BLACK:
self.DELETE_FIXUP(x) curNode = None return False def DELETE_FIXUP(self, x):
p = x.parent
# w:x的兄弟结点
if x == p.left:
w = x.right
else:
w = x.left # case1:x的兄弟w是红色的
if w.color == RED:
p.PAINT(RED)
w.PAINT(BLACK)
if w == p.right:
self.LEFT_ROTATE(p)
else:
self.RIGHT_ROTATE(p) if w.color == BLACK:
# case2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
if w.left.color == BLACK and w.right.color == BLACK:
w.PAINT(RED)
if p.color == BLACK:
return
else:
p.color = BLACK
self.DELETE_FIXUP(p) # case3:x的兄弟w是黑色的,而且w的左儿子是红色的,右儿子是黑色的
if w.left.color == RED and w.color == BLACK:
w.left.PAINT(BLACK)
w.PAINT(RED)
self.RIGHT_ROTATE(w) # case4:x的兄弟w是黑色的,而且w的右儿子是红
if w.right.color == RED:
p.PAINT(BLACK)
w.PAINT(RED)
if w == p.right:
self.LEFT_ROTATE(p)
else:
self.RIGHT_ROTATE(p) def SHOW(self):
self.DISPLAY1(self.root)
return self.zlist def DISPLAY1(self, node):
if node is None:
return
self.DISPLAY1(node.left)
self.zlist.append(node.val)
self.DISPLAY1(node.right) def DISPLAY2(self, node):
if node is None:
return
self.DISPLAY2(node.left)
print node.val,
self.DISPLAY2(node.right) def DISPLAY3(self, node):
if node is None:
return
self.DISPLAY3(node.left)
self.DISPLAY3(node.right)
print node.val, class RBTnode:
'''红黑树的节点类型'''
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.parent = None def PAINT(self, color):
self.color = color def zuoxuan(b, c):
a = b.parent
a.left = c
c.parent = a
b.parent = c
c.left = b if __name__ == '__main__':
rbt=RBT()
b = [] for i in range(100):
m = randint(0, 500)
rbt.INSERT(m)
b.append(m) a = rbt.SHOW()
b.sort()
equal = True
for i in range(100):
if a[i] != b[i]:
equal = False
break if not equal:
print 'wrong'
else:
print 'OK!'

  PS:这篇文章刚写好的时候,代码中是有错误的,而且DELETE_FIXUP()也没有给出;分析中也有小错误。不过现在已经改正了,代码中通过随机生成的数字,用系统的排序和我写的红黑树的排序对比,发现是结果是一样的,所以可以认为前面的算法分析是正确的。不过,速度上其实还是有点慢的,比如我用红黑树去排序,用在一道codeforces的题目中取代系统的sort,结果就会超时。

红黑树:个人理解与Python实现的更多相关文章

  1. 对B+树,B树,红黑树的理解

    出处:https://www.jianshu.com/p/86a1fd2d7406 写在前面,好像不同的教材对b树,b-树的定义不一样.我就不纠结这个到底是叫b-树还是b-树了. 如图所示,区别有以下 ...

  2. 对于AVL树和红黑树的理解

    AVL又称(严格)高度平衡的二叉搜索树,也叫二叉查找树.平衡二叉树.window对进程地址空间的管理用到了AVL树. 红黑树是非严格平衡二叉树,统计性能要好于平衡二叉树.广泛的在C++的STL中,ma ...

  3. 平衡树 - 红黑树(JQuery+Js+Canvas版本的,帮助大家理解)

    红黑树 1.红黑树介绍 年写的一篇论文中获得的.它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的:它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目. 2 ...

  4. JAVA中的数据结构 - 真正的去理解红黑树

    一, 红黑树所处数据结构的位置: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 从二叉树看,红黑树是一颗相对平衡的二叉树 二叉树--&g ...

  5. stl map底层之红黑树插入步骤详解与代码实现

    转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...

  6. JAVA中的数据结构 - 1,红黑树

    背景: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 二叉树-->搜索二叉树-->平衡搜索二叉树-->B树--> ...

  7. 2-3 树/红黑树(red-black tree)

    2-3 tree 2-3树节点: null节点,null节点到根节点的距离都是相同的,所以2-3数是平衡树 2叉节点,有两个分树,节点中有一个元素,左树元素更小,右树元素节点更大 3叉节点,有三个子树 ...

  8. JAVA数据结构之红-黑树

    本篇博客我会重点介绍对红-黑树的理解,重点介绍红-黑树的查找,这里我们将要讨论的算法称为自顶向下插入,也就是把沿着树向下查找插入点 Ⅰ.平衡树和非平衡树 平衡树和非平衡树:当插入一组数据关键字是按照升 ...

  9. HashMap1.7和1.8,红黑树原理!

    jdk 1.7 概述 HashMap基于Map接口实现,元素以键值对的方式存储,并允许使用null键和null值,但只能有一个键作为null,因为key不允许重复,另外HashMap不能保证放入元素的 ...

随机推荐

  1. php static 变量用法

    有时候我们可能需要重复调用一个函数,里面有些变量不需要重复初始化.初始化成本比较高的,我们可以使用 static 关键字修饰,在该变量没有初始化的时候才进行初始化,初始化过的变量就不再初始化.如: f ...

  2. SpringCloud微服务实战-Zuul-APIGateway(十)

    本文转自:http://blog.csdn.net/qq_22841811/article/details/67637786#准备工作 1 API Gateway 2 Zuul介绍 2.1 zuul的 ...

  3. R语言 神经网络算法

    人工神经网络(ANN),简称神经网络,是一种模仿生物神经网络的结构和功能的数学模型或计算模型.神经网络由大量的人工神经元联结进行计算.大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自 ...

  4. bzoj 5015 [Snoi2017]礼物 矩阵乘法

    5015: [Snoi2017]礼物 Time Limit: 15 Sec  Memory Limit: 512 MBSubmit: 163  Solved: 115[Submit][Status][ ...

  5. error: failed to connect to the hypervisor error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory 解决办法

    服务器版本:CentOS Linux release 7.4 Linux lb 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x8 ...

  6. Python高手之路【七】python基础之模块

    本节大纲 模块介绍 time &datetime模块 random os sys shutil json & picle shelve xml处理 yaml处理 configparse ...

  7. Oracle的基本语法,存储函数及触发器

    1.PL/SQL    PL/SQL是Oracle对 ql语言的过程化扩展,指在 SQL 命令语言中增加了过程处理语句,使SQL语言具有过程处理能力.把 SQL 语言的数据操纵能  力与过程语言的数据 ...

  8. Redis slowlog慢查询

    slowlog (阅读原文) 此命令用于读取和重置redis慢请求日志 redis slow log概述 redis的slow log记录了那些执行时间超过规定时长的请求.执行时间不包括I/O操作(比 ...

  9. 算法进阶之Leetcode刷题记录

    目录 引言 题目 1.两数之和 题目 解题笔记 7.反转整数 题目 解题笔记 9.回文数 题目 解题笔记 13.罗马数字转整数 题目 解题笔记 14.最长公共前缀 题目 解题笔记 20.有效的括号 题 ...

  10. hdu 3065病毒侵袭持续中

    病毒侵袭持续中 http://acm.hdu.edu.cn/showproblem.php?pid=3065 Time Limit: 2000/1000 MS (Java/Others)    Mem ...