Python3实现红黑树[上篇]
由于时间有限,这次只写了红黑树添加节点,关于节点的删除放在下一讲 https://blog.csdn.net/qq_18138105/article/details/105324025。
关于红黑树的介绍,来由,性质和定义,可以看这篇文章,本篇就不再赘述了:红黑树,超强动静图详解,简单易懂
关于红黑树,其实比较容易摸不着头脑的是“左旋”和“右旋”。
既然是旋转,那么肯定有支点和旋点,也就是以支点为轴,旋点绕支点 顺时针 或 逆时针旋转。
那么,“左旋” 和 “右旋” 到底哪个是顺时针,哪个是逆时针呢?
在这里,旋点一定是支点的父节点,也就是说旋点的位置比支点高,因此 左旋就是逆时针旋转,右旋就是顺时针旋转!
对于旋转,分享一个口诀:
右旋: 支点占旋点原位,支点的右给旋点作为左,旋点作为支点的右,交换支点和旋点的颜色
左旋: 支点占旋点原位,支点的左给旋点作为右,旋点作为支点的左,交换支点和旋点的颜色
先借用大神的2个图。如下图所示,就是 以p为支点,g右旋的过程,看看是否符合上面的口诀呢: 经过右旋后,支点p占据旋点g的原位,支点p的右T3作为了旋点g的左,旋点g作为了p的右。
再看看左旋:经过左旋后,支点p占据旋点g的原位,支点p的左T3作为了旋点g的右,旋点g作为了p的左。也是符合上面的口诀的。
以下便是python3代码实现 红黑树插入节点的过程。
# 红黑树节点
class RBN(object):
def __init__(self, data):
self.data = data # 数据域
self.color = 0 # 0红 1黑
self.left = None
self.right = None
self.parent = None
# 红黑树
class RBT(object):
def __init__(self):
self.root = None
# 中序遍历
def midTraverse(self, x):
if x == None:
return
self.midTraverse(x.left)
colorStr = '黑' if x.color == 1 else '红'
parentStr = '父=' + ('nil' if x.parent == None else str(x.parent.data))
print(x.data, colorStr, parentStr)
self.midTraverse(x.right)
# 添加一个节点
def add(self, x):
# 如果没有根节点 作为根节点
if self.root == None:
self.root = x
x.color = 1 # 根节点为黑色
# print('添加成功', x.data)
return
# 寻找合适的插入位置
p = self.root
while p != None:
if x.data < p.data:
if p.left == None:
p.left = x
x.parent = p
# print('添加成功', x.data)
self.addFix(x)
break
p = p.left
elif x.data > p.data:
if p.right == None:
p.right = x
x.parent = p
# print('添加成功', x.data)
self.addFix(x)
break
p = p.right
else:
return
# 调整红黑树
def addFix(self, x):
while True:
if x == self.root: # 如果处理到根节点了 则着色为黑
x.color = 1
return
p = x.parent # 爸爸
if p.color == 1 or x.color == 1: # 自己和爸爸只要有一个是黑的 就构不成双红 则返回
return
# 接下来分析红爸爸情况
g = p.parent # 爷爷 红爸爸肯定有爸爸,因为红色绝不是根节点
u = g.left if p == g.right else g.right # 叔叔 叔叔可能为空节点
if u != None and u.color == 0: # 红叔叔 则着色 然后从爷爷开始向上继续调整
u.color = p.color = 1 # 叔叔和爸爸都变黑色
g.color = 0 # 爷爷变红色
x = g # x指向爷爷,然后继续循环
continue
# 接下来分析黑叔叔得情况 有四种情况 左左,左右,右左,右右
if p == g.left and x == p.left: # 左左
# 以爸爸为支点右旋爷爷
self.rotateRight(p)
elif p == g.left and x == p.right: # 左右
# 以x为支点左旋爸爸
self.rotateLeft(x)
# 以x为支点右旋爷爷(上面的旋转把爷爷变成了新爸爸)
self.rotateRight(x)
elif p == g.right and x == p.right: # 右右 其实就是 左左的镜像
# 以爸爸为支点左旋爷爷
self.rotateLeft(p)
elif p == g.right and x == p.left: # 右左 其实就是 左右的镜像
# 以x为支点右旋爸爸
self.rotateRight(x)
# 以x为支点左旋爷爷(上面的旋转把爷爷变成了新爸爸)
self.rotateLeft(x)
#
# 关于红黑树的旋转,一直是个难搞的点
# 这里我提供一个口诀:
# 右旋: 支点占旋点原位,支点的右给旋点作为左,旋点作为支点的右
# 左旋: 支点占旋点原位,支点的左给旋点作为右,旋点作为支点的左
#
# 右旋 p支点
def rotateRight(self, p):
g = p.parent # 支点的父节点就是旋点
# 右旋g
if g == self.root: # 若g是根节点 则p升为根节点
self.root = p
p.parent = None
else: # 若g不是根节点 那么必然存在g.parent p占据g的位置
gp = g.parent
p.parent = gp
if g == gp.left:
gp.left = p
else:
gp.right = p
g.left = p.right
if p.right != None:
p.right.parent = g
p.right = g
g.parent = p
# g和p颜色交换
p.color, g.color = g.color, p.color
# 左旋 p 支点
def rotateLeft(self, p):
g = p.parent # 支点的父节点就是旋点
# 左旋g
if g == self.root: # 若g是根节点 则p升为根节点
self.root = p
p.parent = None
else: # 若g不是根节点 那么必然存在g.parent p占据g的位置
gp = g.parent
p.parent = gp
if g == gp.left:
gp.left = p
else:
gp.right = p
g.right = p.left
if p.left != None:
p.left.parent = g
p.left = g
g.parent = p
# g和p颜色交换
p.color, g.color = g.color, p.color
if __name__ == '__main__':
rbt = RBT()
datas = [10, 20, 30, 15]
# datas = [11, 2, 14, 1, 7, 15, 5, 8, 4]
for x in datas:
rbt.add(RBN(x))
rbt.midTraverse(rbt.root)
关于红黑树节点的删除,请看下回分解。