Python3实现红黑树[上篇]
Python3實現(xiàn)紅黑樹[上篇]
由于時間有限,這次只寫了紅黑樹添加節(jié)點,關(guān)于節(jié)點的刪除放在下一講 https://blog.csdn.net/qq_18138105/article/details/105324025。
關(guān)于紅黑樹的介紹,來由,性質(zhì)和定義,可以看這篇文章,本篇就不再贅述了:紅黑樹,超強動靜圖詳解,簡單易懂
關(guān)于紅黑樹,其實比較容易摸不著頭腦的是“左旋”和“右旋”。
既然是旋轉(zhuǎn),那么肯定有支點和旋點,也就是以支點為軸,旋點繞支點 順時針 或 逆時針旋轉(zhuǎn)。
那么,“左旋” 和 “右旋” 到底哪個是順時針,哪個是逆時針呢?
在這里,旋點一定是支點的父節(jié)點,也就是說旋點的位置比支點高,因此 左旋就是逆時針旋轉(zhuǎn),右旋就是順時針旋轉(zhuǎn)!
對于旋轉(zhuǎn),分享一個口訣:
右旋: 支點占旋點原位,支點的右給旋點作為左,旋點作為支點的右,交換支點和旋點的顏色 左旋: 支點占旋點原位,支點的左給旋點作為右,旋點作為支點的左,交換支點和旋點的顏色先借用大神的2個圖。如下圖所示,就是 以p為支點,g右旋的過程,看看是否符合上面的口訣呢: 經(jīng)過右旋后,支點p占據(jù)旋點g的原位,支點p的右T3作為了旋點g的左,旋點g作為了p的右。
再看看左旋:經(jīng)過左旋后,支點p占據(jù)旋點g的原位,支點p的左T3作為了旋點g的右,旋點g作為了p的左。也是符合上面的口訣的。
以下便是python3代碼實現(xiàn) 紅黑樹插入節(jié)點的過程。
# 紅黑樹節(jié)點 class RBN(object):def __init__(self, data):self.data = data # 數(shù)據(jù)域self.color = 0 # 0紅 1黑self.left = Noneself.right = Noneself.parent = None# 紅黑樹 class RBT(object):def __init__(self):self.root = None# 中序遍歷def midTraverse(self, x):if x == None:returnself.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)# 添加一個節(jié)點def add(self, x):# 如果沒有根節(jié)點 作為根節(jié)點if self.root == None:self.root = xx.color = 1 # 根節(jié)點為黑色# print('添加成功', x.data)return# 尋找合適的插入位置p = self.rootwhile p != None:if x.data < p.data:if p.left == None:p.left = xx.parent = p# print('添加成功', x.data)self.addFix(x)breakp = p.leftelif x.data > p.data:if p.right == None:p.right = xx.parent = p# print('添加成功', x.data)self.addFix(x)breakp = p.rightelse:return# 調(diào)整紅黑樹def addFix(self, x):while True:if x == self.root: # 如果處理到根節(jié)點了 則著色為黑x.color = 1returnp = x.parent # 爸爸if p.color == 1 or x.color == 1: # 自己和爸爸只要有一個是黑的 就構(gòu)不成雙紅 則返回return# 接下來分析紅爸爸情況g = p.parent # 爺爺 紅爸爸肯定有爸爸,因為紅色絕不是根節(jié)點u = g.left if p == g.right else g.right # 叔叔 叔叔可能為空節(jié)點if u != None and u.color == 0: # 紅叔叔 則著色 然后從爺爺開始向上繼續(xù)調(diào)整u.color = p.color = 1 # 叔叔和爸爸都變黑色g.color = 0 # 爺爺變紅色x = g # x指向爺爺,然后繼續(xù)循環(huán)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為支點右旋爺爺(上面的旋轉(zhuǎn)把爺爺變成了新爸爸)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為支點左旋爺爺(上面的旋轉(zhuǎn)把爺爺變成了新爸爸)self.rotateLeft(x)## 關(guān)于紅黑樹的旋轉(zhuǎn),一直是個難搞的點# 這里我提供一個口訣:# 右旋: 支點占旋點原位,支點的右給旋點作為左,旋點作為支點的右# 左旋: 支點占旋點原位,支點的左給旋點作為右,旋點作為支點的左## 右旋 p支點def rotateRight(self, p):g = p.parent # 支點的父節(jié)點就是旋點# 右旋gif g == self.root: # 若g是根節(jié)點 則p升為根節(jié)點self.root = pp.parent = Noneelse: # 若g不是根節(jié)點 那么必然存在g.parent p占據(jù)g的位置gp = g.parentp.parent = gpif g == gp.left:gp.left = pelse:gp.right = pg.left = p.rightif p.right != None:p.right.parent = gp.right = gg.parent = p# g和p顏色交換p.color, g.color = g.color, p.color# 左旋 p 支點def rotateLeft(self, p):g = p.parent # 支點的父節(jié)點就是旋點# 左旋gif g == self.root: # 若g是根節(jié)點 則p升為根節(jié)點self.root = pp.parent = Noneelse: # 若g不是根節(jié)點 那么必然存在g.parent p占據(jù)g的位置gp = g.parentp.parent = gpif g == gp.left:gp.left = pelse:gp.right = pg.right = p.leftif p.left != None:p.left.parent = gp.left = gg.parent = p# g和p顏色交換p.color, g.color = g.color, p.colorif __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)關(guān)于紅黑樹節(jié)點的刪除,請看下回分解。
總結(jié)
以上是生活随笔為你收集整理的Python3实现红黑树[上篇]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吸引人的医美口号文案29句
- 下一篇: Python3求解找到小镇的法官问题