红黑树是一种基于二叉查找树的数据结构,它具有如下性质:
(1) 二叉查找树的性质它都有
(2) 每个节点都有一个颜色属性,每个节点或是红的或是黑的
(3) 根节点必须是黑的
(4) 每个叶子节点(nil节点)为黑
(5) 如果一个节点为红的,那么它的两个孩子都是黑的
(6) 每个节点到它子孙叶子节点的路径上的黑色节点个数是相同的
相比二叉查找树,红黑树在添加或删除元素的同时还需要调整树的深度,所以需要用到对树结构的一些旋转操作,下面的实例代码给的非常详尽了,可以看看LeftRotate()和RightRotate()函数式如何实现旋转的。如果有人发现了BUG请在留言中发表~
这个代码是在之前的"Golang以OO的方式实现二叉查找树"里的代码加工实现的,因为本人技术不到位。。。出了好几次BUG,修修补补,写了这么一大堆代码,大家凑合着看。。。:
- package main
- import (
- "fmt"
- )
- var count int
- 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)
- parent *TreeNode
- revise 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 = nil
- rbt.root = node.lchild
- } else {
- 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 {
- if node == rbt.root {
- node.rchild.parent = nil
- rbt.root = node.rchild
- } else {
- 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
- node.color = successor.color
- deleteNode(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 *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.parent.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.parent.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(node *TreeNode) {
- var brother *TreeNode
- for 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.rchild
- if 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.lchild
- if 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.root
- for {
- 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) 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(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