這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
紅/黑樹狀結構是一種基於二叉尋找樹的資料結構,它具有如下性質:
(1) 二叉尋找樹的性質它都有
(2) 每個節點都有一個顏色屬性,每個節點或是紅的或是黑的
(3) 根節點必須是黑的
(4) 每個葉子節點(nil節點)為黑
(5) 如果一個節點為紅的,那麼它的兩個孩子都是黑的
(6) 每個節點到它子孫葉子節點的路徑上的黑色節點個數是相同的
相比二叉尋找樹,紅/黑樹狀結構在添加或刪除元素的同時還需要調整樹的深度,所以需要用到對樹結構的一些旋轉操作,下面的執行個體代碼給的非常詳盡了,可以看看LeftRotate()和RightRotate()函數式如何?旋轉的。如果有人發現了BUG請在留言中發表~
這個代碼是在之前的"Golang以OO的方式實現二叉尋找樹"裡的代碼加工實現的,因為本人技術不到位。。。出了好幾次BUG,修修補補,寫了這麼一大堆代碼,大家湊合著看。。。:
package mainimport ("fmt")var count inttype TreeNode struct {data float64color string //比二叉尋找樹要多出一個顏色屬性lchild *TreeNoderchild *TreeNodeparent *TreeNode}type RBTree struct {root *TreeNodecur *TreeNodecreate *TreeNode}func (rbt *RBTree) Add(data float64) {rbt.create = new(TreeNode)rbt.create.data = datarbt.create.color = "red"if !rbt.IsEmpty() {rbt.cur = rbt.rootfor {if data < rbt.cur.data {//如果要插入的值比當前節點的值小,則當前節點指向當前節點的左孩子,如果//左孩子為空白,就在這個左孩子上插入新值if rbt.cur.lchild == nil {rbt.cur.lchild = rbt.createrbt.create.parent = rbt.curbreak} else {rbt.cur = rbt.cur.lchild}} else if data > rbt.cur.data {//如果要插入的值比當前節點的值大,則當前節點指向當前節點的右孩子,如果//右孩子為空白,就在這個右孩子上插入新值if rbt.cur.rchild == nil {rbt.cur.rchild = rbt.createrbt.create.parent = rbt.curbreak} else {rbt.cur = rbt.cur.rchild}} else {//如果要插入的值在樹中已經存在,則退出return}}} else {rbt.root = rbt.createrbt.root.color = "black"rbt.root.parent = nilreturn}//插入節點後對紅黑性質進行修複rbt.insertBalanceFixup(rbt.create)}func (rbt *RBTree) Delete(data float64) {var (deleteNode func(node *TreeNode)node *TreeNode = rbt.Search(data)parent *TreeNoderevise string)if node == nil {return} else {parent = node.parent}//下面這小塊代碼用來判斷替代被刪節點位置的節點是哪個後代if node.lchild == nil && node.rchild == nil {revise = "none"} else if parent == nil {revise = "root"} else if node == parent.lchild {revise = "left"} else if node == parent.rchild {revise = "right"}deleteNode = func(node *TreeNode) {if node == nil {return}if node.lchild == nil && node.rchild == nil {//如果要刪除的節點沒有孩子,直接刪掉它就可以(毫無挂念~.~!)if node == rbt.root {rbt.root = nil} else {if node.parent.lchild == node {node.parent.lchild = nil} else {node.parent.rchild = nil}}} else if node.lchild != nil && node.rchild == nil {//如果要刪除的節點只有左孩子或右孩子,讓這個節點的父節點指向它的指標指向它的//孩子即可if node == rbt.root {node.lchild.parent = nilrbt.root = node.lchild} else {node.lchild.parent = node.parentif node.parent.lchild == node {node.parent.lchild = node.lchild} else {node.parent.rchild = node.lchild}}} else if node.lchild == nil && node.rchild != nil {if node == rbt.root {node.rchild.parent = nilrbt.root = node.rchild} else {node.rchild.parent = node.parentif node.parent.lchild == node {node.parent.lchild = node.rchild} else {node.parent.rchild = node.rchild}}} else {//如果要刪除的節點既有左孩子又有右孩子,就把這個節點的直接後繼的值賦給這個節//點,然後刪除直接後繼節點即可successor := rbt.GetSuccessor(node.data)node.data = successor.datanode.color = successor.colordeleteNode(successor)}}deleteNode(node)if node.color == "black" {if revise == "root" {rbt.deleteBalanceFixup(rbt.root)} else if revise == "left" {rbt.deleteBalanceFixup(parent.lchild)} else if revise == "right" {rbt.deleteBalanceFixup(parent.rchild)}}//至於為什麼刪除的為紅節點時不用調節平衡,那本黑色的《演算法導論(第二版)》是這麼解釋的://當刪除紅節點時//(1) 樹中個節點的黑高度都沒有變化//(2) 不存在兩個相鄰的紅色節點//(3) 如果刪除的節點是紅的,就不可能是根,所以根依然是黑色的}//這個函數用於在紅/黑樹狀結構執行插入操作後,修複紅黑性質func (rbt *RBTree) insertBalanceFixup(insertnode *TreeNode) {var uncle *TreeNodefor insertnode.color == "red" && insertnode.parent.color == "red" {//擷取新插入的節點的叔叔節點(與父節點同根的另一個節點)if insertnode.parent == insertnode.parent.parent.lchild {uncle = insertnode.parent.parent.rchild} else {uncle = insertnode.parent.parent.lchild}if uncle != nil && uncle.color == "red" {//如果叔叔節點是紅色,就按照如所示變化(●->黑, ○->紅):// | |// 1● 1○ <-new node ptr come here// / \ --------\ / \// 2○ ○3 --------/ 2● ●3// / ///4○ <-new node ptr 4○////這種情況可以一直迴圈,知道new node ptr指到root時退出(root顏色依然為黑)uncle.color, insertnode.parent.color = "black", "black"insertnode = insertnode.parent.parentif insertnode == rbt.root || insertnode == nil {return}insertnode.color = "red"} else {//如果叔叔節點為空白或叔叔節點為黑色,就按照如所示變化:// | |// 1● <-right rotate 2●// / \ --------\ / \// 2○ ●3 --------/ 4○ ○1// / \//4○ <-new node ptr ●3////當然,這隻是叔叔節點為黑時的一種情況,如果的節點4為2節點的右孩子,則//可以先在2節點處向左旋轉,這樣就轉換成了這種情況了。另外兩種情況自己想//想就明白了if insertnode.parent == insertnode.parent.parent.lchild {if insertnode == insertnode.parent.rchild {insertnode = insertnode.parentrbt.LeftRotate(insertnode)}insertnode = insertnode.parentinsertnode.color = "black"insertnode = insertnode.parentinsertnode.color = "red"rbt.RightRotate(insertnode)} else {if insertnode == insertnode.parent.lchild {insertnode = insertnode.parentrbt.RightRotate(insertnode)}insertnode = insertnode.parentinsertnode.color = "black"insertnode = insertnode.parentinsertnode.color = "red"rbt.LeftRotate(insertnode)}return}}}//這個函數用於在紅/黑樹狀結構執行刪除操作後,修複紅黑性質func (rbt *RBTree) deleteBalanceFixup(node *TreeNode) {var brother *TreeNodefor node != rbt.root && node.color == "black" {//刪除時的紅黑修複需要考慮四種情況(下面的“X”指取代了被刪節點位置的新節點)// (1) X的兄弟節點是紅色的:// | |// 1● 3●// / \ --------\ / \// X-> 2● ○3 <-brother --------/ 1○ ●5// / \ / \// 4● ●5 X-> 2● ●4//// (2) X的兄弟節點是黑色的,而且兄弟節點的兩個孩子都是黑色的:// | |// 1○ X-> 1○// / \ --------\ / \// X-> 2● ●3 <-brother --------/ 2● ○3// / \ / \// 4● ●5 4● ●5//// (3) X的兄弟節點是黑色的,兄弟的左孩子是紅色的,右孩子是黑色的:// | |// 1○ 1○// / \ --------\ / \// X-> 2● ●3 <-brother --------/ X->2● ●4// / \ \// 4○ ●5 ○3// \// ●5//// (4) X的兄弟節點是黑色的,兄弟的右孩子是紅色的:// | |// 1○ 3○ X->root and loop while end// / \ --------\ / \// X-> 2● ●3 <-brother --------/ 1● ●5// / \ / \// 4○ ○5 2● ○4////以上是兄弟節點在X右邊時的情況,在X左邊是取相反即可!if node.parent.lchild == node && node.parent.rchild != nil {brother = node.parent.rchildif brother.color == "red" {brother.color = "black"node.parent.color = "red"rbt.LeftRotate(node.parent)} else if brother.color == "black" && brother.lchild != nil && brother.lchild.color == "black" && brother.rchild != nil && brother.rchild.color == "black" {brother.color = "red"node = node.parent} else if brother.color == "black" && brother.lchild != nil && brother.lchild.color == "red" && brother.rchild != nil && brother.rchild.color == "black" {brother.color = "red"brother.lchild.color = "black"rbt.RightRotate(brother)} else if brother.color == "black" && brother.rchild != nil && brother.rchild.color == "red" {brother.color = "red"brother.rchild.color = "black"brother.parent.color = "black"rbt.LeftRotate(brother.parent)node = rbt.root}} else if node.parent.rchild == node && node.parent.lchild != nil {brother = node.parent.lchildif brother.color == "red" {brother.color = "black"node.parent.color = "red"rbt.RightRotate(node.parent)} else if brother.color == "black" && brother.lchild != nil && brother.lchild.color == "black" && brother.rchild != nil && brother.rchild.color == "black" {brother.color = "red"node = node.parent} else if brother.color == "black" && brother.lchild != nil && brother.lchild.color == "black" && brother.rchild != nil && brother.rchild.color == "red" {brother.color = "red"brother.rchild.color = "black"rbt.LeftRotate(brother)} else if brother.color == "black" && brother.lchild != nil && brother.lchild.color == "red" {brother.color = "red"brother.lchild.color = "black"brother.parent.color = "black"rbt.RightRotate(brother.parent)node = rbt.root}} else {return}}}func (rbt RBTree) GetRoot() *TreeNode {if rbt.root != nil {return rbt.root}return nil}func (rbt RBTree) IsEmpty() bool {if rbt.root == nil {return true}return false}func (rbt RBTree) InOrderTravel() {var inOrderTravel func(node *TreeNode)inOrderTravel = func(node *TreeNode) {if node != nil {inOrderTravel(node.lchild)fmt.Printf("%g ", node.data)inOrderTravel(node.rchild)}}inOrderTravel(rbt.root)}func (rbt RBTree) Search(data float64) *TreeNode {//和Add操作類似,只要按照比當前節點小就往左孩子上拐,比當前節點大就往右孩子上拐的思路//一路找下去,知道找到要尋找的值返回即可rbt.cur = rbt.rootfor {if rbt.cur == nil {return nil}if data < rbt.cur.data {rbt.cur = rbt.cur.lchild} else if data > rbt.cur.data {rbt.cur = rbt.cur.rchild} else {return rbt.cur}}}func (rbt RBTree) GetDeepth() int {var getDeepth func(node *TreeNode) intgetDeepth = func(node *TreeNode) int {if node == nil {return 0}if node.lchild == nil && node.rchild == nil {return 1}var (ldeepth int = getDeepth(node.lchild)rdeepth int = getDeepth(node.rchild))if ldeepth > rdeepth {return ldeepth + 1} else {return rdeepth + 1}}return getDeepth(rbt.root)}func (rbt RBTree) GetMin() float64 {//根據二叉尋找樹的性質,樹中最左邊的節點就是值最小的節點if rbt.root == nil {return -1}rbt.cur = rbt.rootfor {if rbt.cur.lchild != nil {rbt.cur = rbt.cur.lchild} else {return rbt.cur.data}}}func (rbt RBTree) GetMax() float64 {//根據二叉尋找樹的性質,樹中最右邊的節點就是值最大的節點if rbt.root == nil {return -1}rbt.cur = rbt.rootfor {if rbt.cur.rchild != nil {rbt.cur = rbt.cur.rchild} else {return rbt.cur.data}}}func (rbt RBTree) GetPredecessor(data float64) *TreeNode {getMax := func(node *TreeNode) *TreeNode {if node == nil {return nil}for {if node.rchild != nil {node = node.rchild} else {return node}}}node := rbt.Search(data)if node != nil {if node.lchild != nil {//如果這個節點有左孩子,那麼它的直接前驅就是它左子樹的最右邊的節點,因為比這//個節點值小的節點都在左子樹,而這些節點中值最大的就是這個最右邊的節點return getMax(node.lchild)} else {//如果這個節點沒有左孩子,那麼就沿著它的父節點找,知道某個父節點的父節點的右//孩子就是這個父節點,那麼這個父節點的父節點就是直接前驅for {if node == nil || node.parent == nil {break}if node == node.parent.rchild {return node.parent}node = node.parent}}}return nil}func (rbt RBTree) GetSuccessor(data float64) *TreeNode {getMin := func(node *TreeNode) *TreeNode {if node == nil {return nil}for {if node.lchild != nil {node = node.lchild} else {return node}}}//參照尋找直接前驅的函數對比著看node := rbt.Search(data)if node != nil {if node.rchild != nil {return getMin(node.rchild)} else {for {if node == nil || node.parent == nil {break}if node == node.parent.lchild {return node.parent}node = node.parent}}}return nil}func (rbt *RBTree) Clear() {rbt.root = nilrbt.cur = nilrbt.create = nil}/** * 旋轉圖解(以向左旋轉為例): * | | * ○ <-left rotate ● * \ ----------\ / \ * ● ----------/ ○ ●r * / \ \ * l● ●r l● * * * * | | * ○ <-left rotate ● * \ ----------\ / \ * ● ----------/ ○ ● * \ \ * ● nil <-don't forget it should be nil */func (rbt *RBTree) LeftRotate(node *TreeNode) {if node.rchild == nil {return}right_child := node.rchild//將要旋轉的節點的右孩子的左孩子賦給這個節點的右孩子,這裡最好按如下3行代碼的順序寫,//否則該節點的右孩子的左孩子為nil時,很容易忘記把這個節點的右孩子也置為nil.node.rchild = right_child.lchildif node.rchild != nil {node.rchild.parent = node}//讓要旋轉的節點的右孩子的父節點指標指向當前節點父節點。如果父節點是根節點要特別處理right_child.parent = node.parentif node.parent == nil {rbt.root = right_child} else {if node.parent.lchild == node {node.parent.lchild = right_child} else {node.parent.rchild = right_child}}//上面的準備工作完畢了,就可以開始旋轉了,讓要旋轉的節點的右孩子的左孩子指向該節點,//別忘了把這個被旋轉的節點的父節點指標指向新的父節點right_child.lchild = nodenode.parent = right_child}func (rbt *RBTree) RightRotate(node *TreeNode) {//向右旋轉的過程與LeftRotate()正好相反if node.lchild == nil {return}left_child := node.lchildnode.lchild = left_child.rchildif node.lchild != nil {node.lchild.parent = node}left_child.parent = node.parentif node.parent == nil {rbt.root = left_child} else {if node.parent.lchild == node {node.parent.lchild = left_child} else {node.parent.rchild = left_child}}left_child.rchild = nodenode.parent = left_child}func main() {var rbt RBTreerbt.Add(9)rbt.Add(8)rbt.Add(7)rbt.Add(6)rbt.Add(5)rbt.Add(4)rbt.Add(3)rbt.Add(2)rbt.Add(1)fmt.Println(rbt.GetDeepth())}
運行結果如下:
如果是二叉尋找樹,那麼樹的深度就會為9,這樣就和普通的線性結構沒什麼區別了,可見紅/黑樹狀結構的確是一棵更好的二叉尋找樹。
如果轉載請註明出處:http://blog.csdn.net/gophers/article/details/23608025