红黑树是一种基于二叉查找树的数据结构,它具有如下性质:
(1) 二叉查找树的性质它都有
(2) 每个节点都有一个颜色属性,每个节点或是红的或是黑的
(3) 根节点必须是黑的
(4)
每个叶子节点(nil节点)为黑
(5)
如果一个节点为红的,那么它的两个孩子都是黑的
(6) 每个节点到它子孙叶子节点的路径上的黑色节点个数是相同的
相比二叉查找树,红黑树在添加或删除元素的同时还需要调整树的深度,所以需要用到对树结构的一些旋转操作,下面的实例代码给的非常详尽了,可以看看LeftRotate()和RightRotate()函数式如何实现旋转的。如果有人发现了BUG请在留言中发表~
这个代码是在之前的"Golang以OO的方式实现二叉查找树"里的代码加工实现的:
package main import ( "fmt" ) type TreeNode struct { data float64 color string //比二叉查找树要多出一个颜色属性 lchild *TreeNode rchild *TreeNode parent *TreeNode } type RBTree struct { root *TreeNode cur *TreeNode create *TreeNode } func (rbt *RBTree) Add(data float64) { rbt.create = new(TreeNode) rbt.create.data = data rbt.create.color = "red" if !rbt.IsEmpty() { rbt.cur = rbt.root for { if data < rbt.cur.data { //如果要插入的值比当前节点的值小,则当前节点指向当前节点的左孩子,如果 //左孩子为空,就在这个左孩子上插入新值 if rbt.cur.lchild == nil { rbt.cur.lchild = rbt.create rbt.create.parent = rbt.cur break } else { rbt.cur = rbt.cur.lchild } } else if data > rbt.cur.data { //如果要插入的值比当前节点的值大,则当前节点指向当前节点的右孩子,如果 //右孩子为空,就在这个右孩子上插入新值 if rbt.cur.rchild == nil { rbt.cur.rchild = rbt.create rbt.create.parent = rbt.cur break } else { rbt.cur = rbt.cur.rchild } } else { //如果要插入的值在树中已经存在,则退出 return } } } else { rbt.root = rbt.create rbt.root.color = "black" rbt.root.parent = nil return } //插入节点后对红黑性质进行修复 rbt.insertBalanceFixup(rbt.create) } func (rbt *RBTree) Delete(data float64) { var ( deleteNode func(node *TreeNode) node *TreeNode = rbt.Search(data) ) deleteNode = func(node *TreeNode) { if node.lchild == nil && node.rchild == nil { //如果要删除的节点没有孩子,直接删掉它就可以(毫无挂念~.~!) if node.parent.lchild == node { node.parent.lchild = nil } else { node.parent.rchild = nil } } else if node.lchild != nil && node.rchild == nil { //如果要删除的节点只有左孩子或右孩子,让这个节点的父节点指向它的指针指向它的 //孩子即可 node.lchild.parent = node.parent if node.parent.lchild == node { node.parent.lchild = node.lchild } else { node.parent.rchild = node.lchild } } else if node.lchild == nil && node.rchild != nil { node.rchild.parent = node.parent if node.parent.lchild == node { node.parent.lchild = node.rchild } else { node.parent.rchild = node.rchild } } else { //如果要删除的节点既有左孩子又有右孩子,就把这个节点的直接后继的值赋给这个节 //点,然后删除直接后继节点即可 successor := rbt.GetSuccessor(node.data) node.data = successor.data deleteNode(successor) } } deleteNode(node) } //这个函数用于在红黑树执行插入操作后,修复红黑性质 func (rbt *RBTree) insertBalanceFixup(insertnode *TreeNode) { var uncle *TreeNode for 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.parent if 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.rchild { insertnode = insertnode.parent rbt.LeftRotate(insertnode) } insertnode = insertnode.parent insertnode.color = "black" insertnode = insertnode.parent insertnode.color = "red" rbt.RightRotate(insertnode) } else { if insertnode == insertnode.lchild { insertnode = insertnode.parent rbt.RightRotate(insertnode) } insertnode = insertnode.parent insertnode.color = "black" insertnode = insertnode.parent insertnode.color = "red" rbt.LeftRotate(insertnode) } return } } } //这个函数用于在红黑树执行删除操作后,修复红黑性质 func (rbt *RBTree) deleteBalanceFixup() { //稍后不上 } 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.root for { 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 } if rbt.cur == nil { return nil } } } func (rbt RBTree) GetDeepth() int { var getDeepth func(node *TreeNode) int getDeepth = 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.root for { 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.root for { 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 = nil rbt.cur = nil rbt.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.lchild if node.rchild != nil { node.rchild.parent = node } //让要旋转的节点的右孩子的父节点指针指向当前节点父节点。如果父节点是根节点要特别处理 right_child.parent = node.parent if 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 = node node.parent = right_child } func (rbt *RBTree) RightRotate(node *TreeNode) { //向右旋转的过程与LeftRotate()正好相反 if node.lchild == nil { return } left_child := node.lchild node.lchild = left_child.rchild if node.lchild != nil { node.lchild.parent = node } left_child.parent = node.parent if 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 = node node.parent = left_child } func main() { var rbt RBTree rbt.Add(10) 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()) }
运行结果如下:
如果是二叉查找树,那么树的深度就会为10,这样就和普通的线性结构没什么区别了,可见红黑树的确是一棵更好的二叉查找树。
如果转载请注明出处:http://blog.csdn.net/gophers/article/details/23608025
原文:http://blog.csdn.net/gophers/article/details/23608025