红黑树原理详解及golang实现

文章目录

  • 红黑树原理详解及golang实现
    • 二叉查找树
      • 性质
    • 红黑树
      • 性质
      • operation
      • 红黑树的插入
        • `情形1`:空树
        • `情形2`:插入节点父节为黑色,
        • `情形3` 插入节点的父节点为红色,父节点为父父节点的左孩子,父父节点的右孩子为黑色,插入节点为左孩子(或者父节点为父父节点的右孩子,父父节点的左孩子为黑色,插入节点为右孩子)。
        • `情形4` 插入节点父节点为红色,父父节点的左/右孩子为红色
        • `情形5` 插入节点的父节点为红色,父节点为父父节点的左孩子,父父节点的右孩子为黑色,插入节点为右孩子(或者父节点为父父节点的右孩子,父父节点的左孩子为黑色,插入节点为左孩子)。
    • golang实现
      • 类型定义
      • leftRotate
      • RightRotate
      • Item Interface
      • insert
      • 完整代码
    • 小结

在看红黑树原理之前,先看下二叉查找树。

二叉查找树

二叉查找树,又称二叉排序树,二叉搜索树。

性质

它具备以下性质:

1、左子树上的所有节点均小于它的根节点值。
2、右子树上的所有节点的值均大于它根节点的值。
3、左右子树也分别为二叉排序树。
4、没有键值相等的节点。

既然叫搜索树,那这种结构的好处当然也就是搜索咯,
假如我们要查找15

1、从root节点开始,15<50,找左子树。
2、15<20,找左子树,
3、15>10,找右子树,这样便找到15了。

插入也是类似方法,一层一层比较大小,找到合适的位置插入。

时间复杂度
看见它查找的次数等同于树的高度,在最好的情况下,其平均查找次数和log 2 (n)成正比。
当然也有坏情况,当先后插入的关键字有序时,构成的二叉排序树蜕变为单支树,树的深度和其节点数成正比(和顺序查找相同).
例如依序插入 : 100、200、90、80、70、60、50、40
就会成为如下图形态:

为了解决这种不平衡的情形,就有了红黑树。

红黑树

性质

红黑树是一种自平衡的二叉搜索树,它包含了二叉搜索树的特性,同时具备以下性质:

1、所有节点的颜色不是红色就是黑色。
2、根节点是黑色。
3、每个叶子节点都是黑色的空节点(nil)。
4、每个红色节点的两个子节点都是黑色。(从每个叶子到根节点的所有路径上不能有两个连续的红色节点)
5、从任一节点到其叶子节点的所有路径上都包含相同数目的黑节点。

前四都能理解其意思吧,所以只解释下第五点,比如60这个节点,到其所有叶子节点的路径都只包含1个黑色节点:40和90。

operation

红黑树为了维持它的这5点性质,于是它支持了这么几个操作 ,

变色 : 顾名思义变色,红变黑,黑变红。
左旋转 : 这里借用百度百科两张旋转图,以图中红色节点为中心,中心节点为右孩子替代,而自己成为它的左孩子,同时节点b作为pivot的有孩子(至于为什么是右孩子,b原本就在pivot的右子树上,所以肯定大于pivot)。

右选装 : 同左旋转,中心点顺时钟旋转,成为其原来左孩子的右孩子,原来左孩子的右孩子则成为原中心点的左孩子。

接着看看红黑树的插入,看看它是如何通过这几个op维持红黑树这5个性质的。

红黑树的插入

关于插入的特点 : 由于性质5的约束,每次插入的节点颜色必然为红色。

插入的化存在几种情形,复杂的树可能会涉及到循环的向树上检索做自平衡,这里先从一颗空树开始先简单理解下这些情形。

情形1:空树

直接插入,直接作为根节点,同时由于性质1的约束,通过变色op变为黑色即可。

情形2:插入节点父节为黑色,

不违反任何性质,无需做任何修改。

情形3 插入节点的父节点为红色,父节点为父父节点的左孩子,父父节点的右孩子为黑色,插入节点为左孩子(或者父节点为父父节点的右孩子,父父节点的左孩子为黑色,插入节点为右孩子)。

这是一种插入节点和父节点在一个方向上的情况(例如父节点为左孩子,插入节点也为左孩子)和情形5相反

父节点 及 父父节点变色,再进行左/右旋转, 具体左还是右看你插入的节点的父节点是左子树还是右子树,图例为左子树。

此处 : 变色 - > 右旋转

情形4 插入节点父节点为红色,父父节点的左/右孩子为红色

先将父节点和父父节点右孩子变黑,父父节点变红,然后将父节点当做新插入的红色节点一样递归向上进行平衡红黑树性质操作。 若父节点为根节点直接变父节点为黑色即可.

此处 : 变色 -> 变色

情形5 插入节点的父节点为红色,父节点为父父节点的左孩子,父父节点的右孩子为黑色,插入节点为右孩子(或者父节点为父父节点的右孩子,父父节点的左孩子为黑色,插入节点为左孩子)。

和情形3类比是一种反向情况,这种情况进行两次旋转,
先左/右旋转,旋转后变成了情形3,接着按情形3变换即可。

此处 :左旋转 -> 变色 -> 右旋转

golang实现

类型定义

需要注意的是 红黑树的NIL节点需要单独定义出来,不能直接用nil哦。

const (RED = trueBLACK = false
)type Node struct {Parent *NodeLeft   *NodeRight  *Nodecolor  boolItem
}type Rbtree struct {NIL  *Noderoot *Nodecount uint64
}func New() *Rbtree{node := Node{nil, nil, nil, BLACK, nil}return &Rbtree{NIL   : &node,root  : &node,count : 0,}
}

leftRotate

// Left Rotate
func (rbt *Rbtree) LeftRotate(no *Node) {// Since we are doing the left rotation, the right child should *NOT* nil.if no.Right == nil {return}//          |                                  |//          X                                  Y//         / \         left rotate            / \//        α  Y       ------------->         X   γ//           / \                            / \//          β  γ                            α  βrchild := no.Rightno.Right = rchild.Leftif rchild.Left != nil {rchild.Left.Parent = no}rchild.Parent = no.Parentif no.Parent == nil {rbt.root = rchild} else if no == no.Parent.Left {no.Parent.Left = rchild} else {no.Parent.Right = rchild}rchild.Left = nono.Parent = rchild}func LeftRotateTest(){var i10 Int = 10var i12 Int = 12rbtree := New()x := &Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, BLACK, i10}rbtree.root = xy := &Node{rbtree.root.Right, rbtree.NIL, rbtree.NIL, RED, i12}rbtree.root.Right = ylog.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)rbtree.LeftRotate(rbtree.root)log.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)}

RightRotate

// Right Rotate
func (rbt *Rbtree) RightRotate(no *Node) {if no.Left == nil {return}//          |                                  |//          X                                  Y//         / \         right rotate           / \//        Y   γ      ------------->         α  X//       / \                                    / \//      α  β                                    β  γlchild := no.Leftno.Left = lchild.Rightif lchild.Right != nil {lchild.Right.Parent = no}lchild.Parent = no.Parentif no.Parent == nil {rbt.root = lchild} else if no == no.Parent.Left {no.Parent.Left = lchild} else {no.Parent.Right = lchild}lchild.Right = nono.Parent = lchild}func RightRotateTest(){var i10 Int = 10var i12 Int = 12rbtree := New()x := &Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, BLACK, i10}rbtree.root = xy := &Node{rbtree.root.Right, rbtree.NIL, rbtree.NIL, RED, i12}rbtree.root.Right = ylog.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)rbtree.RightRotate(rbtree.root)log.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)}

Item Interface

值类型接口

type Item interface {Less(than Item) bool
}type Int intfunc (x Int) Less(than Item) bool {log.Println(x, " ", than.(Int))return x < than.(Int)
}type Uint32 uint32func (x Uint32) Less(than Item) bool {log.Println(x, " ", than.(Uint32))return x < than.(Uint32)
}type String stringfunc (x String) Less(than Item) bool {log.Println(x, " ", than.(String))return x < than.(String)
}func ItemTest(){var itype1 Int = 10var itype2 Int = 12log.Println(itype1.Less(itype2))var strtype1 String = "sola"var strtype2 String = "ailumiyana"log.Println(strtype1.Less(strtype2))
}

insert

func (rbt *Rbtree) Insert(no *Node) {x := rbt.rootvar y *Node = rbt.NILfor x != rbt.NIL {y = x if less(no.Item, x.Item) {x = x.Left} else if less(x.Item, no.Item) {x = x.Right} else {log.Println("that node already exist")}}no.Parent = yif y == rbt.NIL {rbt.root = no} else if less(no.Item, y.Item) {y.Left = no} else {y.Right = no}rbt.count++rbt.insertFixup(no)}func (rbt *Rbtree) insertFixup(no *Node) {for no.Parent.color == RED {if no.Parent == no.Parent.Parent.Left {y := no.Parent.Parent.Rightif y.color == RED {//// 情形 4log.Println("TRACE Do Case 4 :", no.Item)no.Parent.color = BLACKy.color = BLACKno.Parent.Parent.color = REDno = no.Parent.Parent  //循环向上自平衡.} else {if no == no.Parent.Right {//// 情形 5 : 反向情形// 直接左旋转 , 然后进行情形3(变色->右旋)log.Println("TRACE Do Case 5 :", no.Item)if no == no.Parent.Right {no = no.Parentrbt.LeftRotate(no)}}log.Println("TRACE Do Case 6 :", no.Item)no.Parent.color = BLACKno.Parent.Parent.color = REDrbt.RightRotate(no.Parent.Parent)}} else { //为父父节点右孩子情形,和左孩子一样,改下转向而已.y := no.Parent.Parent.Leftif y.color == RED {no.Parent.color = BLACKy.color = BLACKno.Parent.Parent.color = REDno = no.Parent.Parent} else {if no == no.Parent.Left {no = no.Parentrbt.RightRotate(no)}no.Parent.color = BLACKno.Parent.Parent.color = REDrbt.LeftRotate(no.Parent.Parent)}}}rbt.root.color = BLACK
}func InsertTest(){rbtree := New()rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(10)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(9)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(8)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(6)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(7)})log.Println("rbtree counts : ", rbtree.count)log.Println("------ ", rbtree.root.Item)log.Println("----", rbtree.root.Left.Item, "---", rbtree.root.Right.Item)log.Println("--", rbtree.root.Left.Left.Item, "-", rbtree.root.Left.Right.Item)}

InsertTest() 仔细瞧瞧这就是我们 讲情形那棵树 哈 。

完整代码

package mainimport("log"
)const (RED = trueBLACK = false
)//-----------------------------------
//Item interface
//
type Item interface {Less(than Item) bool
}type Int intfunc (x Int) Less(than Item) bool {log.Println(x, " ", than.(Int))return x < than.(Int)
}type Uint32 uint32func (x Uint32) Less(than Item) bool {log.Println(x, " ", than.(Uint32))return x < than.(Uint32)
}type String stringfunc (x String) Less(than Item) bool {log.Println(x, " ", than.(String))return x < than.(String)
}//-----------------------------------type Node struct {Parent *NodeLeft   *NodeRight  *Nodecolor  boolItem
}type Rbtree struct {NIL  *Noderoot *Nodecount uint64
}func New() *Rbtree{node := &Node{nil, nil, nil, BLACK, nil}return &Rbtree{NIL   : node,root  : node,count : 0,}
}func less(x, y Item) bool {return x.Less(y)
}// Left Rotate
func (rbt *Rbtree) LeftRotate(no *Node) {// Since we are doing the left rotation, the right child should *NOT* nil.if no.Right == rbt.NIL {return}//          |                                  |//          X                                  Y//         / \         left rotate            / \//        α  Y       ------------->         X   γ//           / \                            / \//          β  γ                            α  βrchild := no.Rightno.Right = rchild.Leftif rchild.Left != rbt.NIL {rchild.Left.Parent = no}rchild.Parent = no.Parentif no.Parent == rbt.NIL {rbt.root = rchild} else if no == no.Parent.Left {no.Parent.Left = rchild} else {no.Parent.Right = rchild}rchild.Left = nono.Parent = rchild}// Right Rotate
func (rbt *Rbtree) RightRotate(no *Node) {if no.Left == rbt.NIL {return}//          |                                  |//          X                                  Y//         / \         right rotate           / \//        Y   γ      ------------->         α  X//       / \                                    / \//      α  β                                    β  γlchild := no.Leftno.Left = lchild.Rightif lchild.Right != rbt.NIL {lchild.Right.Parent = no}lchild.Parent = no.Parentif no.Parent == rbt.NIL {rbt.root = lchild} else if no == no.Parent.Left {no.Parent.Left = lchild} else {no.Parent.Right = lchild}lchild.Right = nono.Parent = lchild}func (rbt *Rbtree) Insert(no *Node) {x := rbt.rootvar y *Node = rbt.NILfor x != rbt.NIL {y = x if less(no.Item, x.Item) {x = x.Left} else if less(x.Item, no.Item) {x = x.Right} else {log.Println("that node already exist")}}no.Parent = yif y == rbt.NIL {rbt.root = no} else if less(no.Item, y.Item) {y.Left = no} else {y.Right = no}rbt.count++rbt.insertFixup(no)}func (rbt *Rbtree) insertFixup(no *Node) {for no.Parent.color == RED {if no.Parent == no.Parent.Parent.Left {y := no.Parent.Parent.Rightif y.color == RED {//// 情形 4log.Println("TRACE Do Case 4 :", no.Item)no.Parent.color = BLACKy.color = BLACKno.Parent.Parent.color = REDno = no.Parent.Parent  //循环向上自平衡.} else {if no == no.Parent.Right {//// 情形 5 : 反向情形// 直接左旋转 , 然后进行情形3(变色->右旋)log.Println("TRACE Do Case 5 :", no.Item)if no == no.Parent.Right {no = no.Parentrbt.LeftRotate(no)}}log.Println("TRACE Do Case 6 :", no.Item)no.Parent.color = BLACKno.Parent.Parent.color = REDrbt.RightRotate(no.Parent.Parent)}} else { //为父父节点右孩子情形,和左孩子一样,改下转向而已.y := no.Parent.Parent.Leftif y.color == RED {no.Parent.color = BLACKy.color = BLACKno.Parent.Parent.color = REDno = no.Parent.Parent} else {if no == no.Parent.Left {no = no.Parentrbt.RightRotate(no)}no.Parent.color = BLACKno.Parent.Parent.color = REDrbt.LeftRotate(no.Parent.Parent)}}}rbt.root.color = BLACK
}func LeftRotateTest(){var i10 Int = 10var i12 Int = 12rbtree := New()x := &Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, BLACK, i10}rbtree.root = xy := &Node{rbtree.root.Right, rbtree.NIL, rbtree.NIL, RED, i12}rbtree.root.Right = ylog.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)rbtree.LeftRotate(rbtree.root)log.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)}func RightRotateTest(){var i10 Int = 10var i12 Int = 12rbtree := New()x := &Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, BLACK, i10}rbtree.root = xy := &Node{rbtree.root.Right, rbtree.NIL, rbtree.NIL, RED, i12}rbtree.root.Left = ylog.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)rbtree.RightRotate(rbtree.root)log.Println("root : ", rbtree.root)log.Println("left : ", rbtree.root.Left)log.Println("right : ", rbtree.root.Right)}func ItemTest(){var itype1 Int = 10var itype2 Int = 12log.Println(itype1.Less(itype2))var strtype1 String = "sola"var strtype2 String = "ailumiyana"log.Println(strtype1.Less(strtype2))
}func InsertTest(){rbtree := New()rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(10)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(9)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(8)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(6)})rbtree.Insert(&Node{rbtree.NIL, rbtree.NIL, rbtree.NIL, RED, Int(7)})log.Println("rbtree counts : ", rbtree.count)log.Println("------ ", rbtree.root.Item)log.Println("----", rbtree.root.Left.Item, "---", rbtree.root.Right.Item)log.Println("--", rbtree.root.Left.Left.Item, "-", rbtree.root.Left.Right.Item)}func main()  {log.Println(" ---- main ------ ")LeftRotateTest()RightRotateTest()ItemTest()InsertTest()
}

小结

好了本文 对红黑树的讲解到此结束,刚开始看红黑树的时候这些性质确实特别绕,但是理解了这5点性质,就好多了。 然后就是两个操作 : 变色旋转 理解红黑树是通过他们进行自平衡的就行了。
由于时间原因只写了插入了 ,没做删除,有机会再补上吧,不过理解了插入原理,删除也不在话下了吧。

红黑树原理详解及golang实现相关推荐

  1. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

  2. STL源码剖析---红黑树原理详解上

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7740956 一.红黑树概述 红黑树和我们以前学过的AVL树类似,都是在进 ...

  3. STL源码剖析---红黑树原理详解

    红黑树概述 红黑树都是在进行插入和删除时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.红黑树追求的时局部平衡而不是AVL树中的非常严格的平衡. 所谓红黑树,不仅是一个二叉搜索树,而且必须满 ...

  4. 红黑树原理详解及代码实现

    文章目录 1. 红黑树概念 2. 节点定义 3. 旋转操作 4. 插入操作 5. 删除操作 6. 完整代码 1. 红黑树概念 下图就是一棵红黑树: 为了后续操作中不出现空指针异常,可以加入一个额外的哨 ...

  5. 红黑树操作详解——很形象的过程

    红黑树是一种很好的自平衡二叉排序树,在此,给出一个网友给出的红黑树操作详解: https://segmentfault.com/a/1190000012728513 里面给出了红黑树的详细操作,过程很 ...

  6. [Java 8 HashMap 详解系列]7.HashMap 中的红黑树原理

    [Java 8 HashMap 详解系列] 文章目录 1.HashMap 的存储数据结构 2.HashMap 中 Key 的 index 是怎样计算的? 3.HashMap 的 put() 方法执行原 ...

  7. 一、epoll原理详解

    Linux服务器开发/后台架构师知识体系整理 一.epoll原理详解 当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll ...

  8. 【Java基础】HashMap原理详解

    [Java基础]HashMap原理详解 HashMap的实现 1. 数组 2.线性链表 3.红黑树 3.1概述 3.2性质 4.HashMap扩容死锁 5. BATJ一线大厂技术栈 HashMap的实 ...

  9. Android面试Hash原理详解二

    Hash系列目录 Android面试Hash原理详解一 Android面试Hash原理详解二 Android面试Hash常见算法 Android面试Hash算法案例 Android面试Hash原理详解 ...

最新文章

  1. 【白话设计模式二】外观模式(Facade)
  2. php基础知识(2),php基础知识学习(二)
  3. C#中获取指定路径下指定后缀名的所有文件的路径的list
  4. Windows 蠕虫首次现身 Mac 电脑:伪装成 Adobe Flash 软件
  5. docker logs 容器日志文件路径查看
  6. 解决jupyter notebook的kernel error内核启动失败问题
  7. 【ArcGIS风暴】ArcGIS中国地表覆盖数据GlobeLand30预处理(批量投影、拼接、掩膜提取)附成品下载
  8. 4步精准诊断“门店数字化”问题,助力门店起死回生
  9. 提交日期表单状态操作_奇怪的知识又增加了,表单还能查寝?
  10. Raid技术精简总结
  11. 20200908:链表类题目集合上
  12. thymeleaf 中 通用的分页方法
  13. VS2017 CUDA编程学习实例3:CUDA实现直方图统计
  14. CSDN早报-2019-04-29
  15. matlab生成非方阵的范德蒙矩阵
  16. 凛冬已至:大厂裁员浪潮,基础福利大砍,行业饱和,大龄程序员该如何自处
  17. 如何编写爬虫获取淘宝网上所有的商品分类以及关键属性 销售属性 非关键属性数据
  18. php设置个性域名,利用nginx泛域名解析配置二级域名和多域名,实现二级域名子站,用户个性独立子域名。...
  19. 第十二章:(1)Fork/Join 分支合并框架
  20. 三大运营商将重新划分4G版图

热门文章

  1. 人人代码生成器中读取配置文件中文乱码解决
  2. 2018年为何众多巨匠逝世?你想到了什么
  3. 不小心删除了docker容器怎么恢复?
  4. Attention中softmax的梯度消失及scaled原因
  5. mysql 类似于check_【小白福利—初级DBA入门必看】MySQL常用工具介绍(六)——客户端工具MySQL_check...
  6. 全局变量与静态全局变量的区别
  7. python进展_Python进展路径 - 从学徒到大师
  8. 软件测试笔记_15_Appium自动化测试框架、操作API
  9. pyplot.scatter函数介绍
  10. mysql sock_mysql.sock的作用