文章目录

  • 左倾红黑树的定义
  • 红黑树性质
  • Node数据结构
  • 旋转
  • 插入
  • 颜色转换
  • 删除
  • 实现
    • Keys
    • Contains
    • DeleteMin、DeleteMax
    • Rank、Get
    • Ceil
    • Floor
    • 例题

左倾红黑树的定义

含有红黑链接(边)并满足下列条件的二叉查找树:

  • 红链接均为左链接
  • 没有任何一个结点同时和两条红链接相连;
  • 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

红链接将两个2-结点(普通二叉树结点)链接起来构成一个3-结点(含有两个键,3个链接);黑链接则是2-3树中的普通链接。

满足这样定义的红黑树和相应的2-3树是一一对应的。 将红链接画平时,一颗红黑树就是一颗2-3树

红黑树性质

(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),性质5保证红黑树中最大长度不超过最小长度2倍 。(最小长度全部由黑结点组成,最大长度由红黑结点交替构成)。

将左倾红黑树转化为2-3树就能明白上述性质。

Node数据结构

将链接的颜色保存在表示结点的Node数据类型的布尔变量color中;如果指向它的链接是红色的,那么该变量为true,黑色则为false。

当我们提到一个结点的颜色时,指的是指向该结点的链接的颜色。

    type Color booltype Key stringtype Value intconst (RED   = trueBLACK = false)type RBT struct {root *Node}// 红链接将两个2-结点链接起来构成一个3-结点// 黑链接则是2-3树中的普通链接。type Node struct {key         Keyval         Valueleft, right *Nodecolor       Color // 由其父结点指向它的链接的颜色,true-红链接,false-黑链接n           int   // 这棵子树中的结点总书}func NewNode(key Key, val Value, n int, color Color) *Node {return &Node{key:   key,val:   val,color: color,n:     n,}}

旋转


插入

新插入的结点,总是用红链接将其与它的父结点相连;

插入新结点后:

  1. 使得父结点有两条红链接,那么直接将其转为两个黑链接即可;(0次旋转)
  2. 如果产生一条红色的右链接,就对父结点执行左旋操作;(1次旋转)
  3. 如果产生连续的两条红右链接,就对上层的红链接执行一次右旋转,转为情况1;(1次旋转)
  4. 如果产生连续的两条连续的红链接,一条红色左链接,一条红色右链接;只需将下层的红色右链接执行一次左旋转,转为情况3;(2次旋转)


颜色转换

删除

参考:

  • 从2-3树谈到左倾红黑树_Addyz的博客-CSDN博客,这里详细介绍了删除代码的优化过程,有助于理解。
  • 有人能讲清楚《Algorithms》中左倾红黑树(LLRB)删除操作的每一行代码吗?

实现

    package red_blacktype Color booltype Key inttype Value intconst (RED   = trueBLACK = false)type RBT struct {root *Node}// 红链接将两个2-结点链接起来构成一个3-结点// 黑链接则是2-3树中的普通链接。type Node struct {key         Keyval         Valueleft, right *Nodecolor       Color // 由其父结点指向它的链接的颜色,true-红链接,false-黑链接size        int}func NewNode(key Key, val Value, color Color, size int) *Node {return &Node{key:   key,val:   val,left:  nil,right: nil,color: color,size:  size,}}func isRed(node *Node) bool {if node == nil {return false}return node.color == RED}func (this *RBT) isEmpty() bool {return this.root == nil}func size(h *Node) int {if h == nil {return 0}return h.size}func (this *RBT) Size() int {return size(this.root)}func findMin(h *Node) *Node {for h.left != nil {h = h.left}return h}// 左旋func rotateLeft(h *Node) *Node {x := h.lefth.left = x.rightx.right = hx.color = h.colorh.color = REDx.size = h.sizeh.size = 1 + size(h.left) + size(h.right)return x}// 右旋func rotateRight(h *Node) *Node {x := h.righth.right = x.leftx.left = hx.color = h.colorh.color = REDx.size = h.sizeh.size = 1 + size(h.left) + size(h.right)return x}func flipsColor(h *Node) {h.color = !h.colorh.left.color = !h.left.colorh.right.color = !h.right.color}func fixUp(h *Node) *Node {// 存在红色右链接,需要右转if !isRed(h.left) && isRed(h.right) {h = rotateRight(h)}// 存在两天连续的红色左链接,需要左转if isRed(h.left) && isRed(h.left.left) {h = rotateLeft(h)}if isRed(h.left) && isRed(h.right) {flipsColor(h)}h.size = 1 + size(h.left) + size(h.right)return h}func (this *RBT) Put(key Key, val Value) {this.root = put(this.root, key, val)this.root.color = BLACK}func put(h *Node, key Key, val Value) *Node {if h == nil {return NewNode(key, val, RED, 1)}if key < h.key {h.left = put(h.left, key, val)} else if key > h.key {h.right = put(h.right, key, val)} else {h.key = keyh.val = val}return fixUp(h)}// 让左孩子结点变红,即3-结点func moveRedLeft(h *Node) *Node {flipsColor(h)// 如果h的右子结点是一个2-结点,只用反转颜色即可// 若h的右子结点是个3-结点,需要从h的右子结点借一个给h的左子结点if isRed(h.right.left) {// 左旋右子节点h.right = rotateLeft(h.right)// 右旋hh = rotateRight(h)// 颜色翻转flipsColor(h)}return h}// 让右子结点变成红结点,即3-结点func moveRedRight(h *Node) *Node {flipsColor(h)// h.right是一个2-结点// h.left是h.right的兄弟结点,且h.left是一个3-结点,需要借给h.left一个键// 这里只进行一次左旋即可if isRed(h.left.left) {h = rotateLeft(h)flipsColor(h)}return h}// delete 就是在查找路径上进行和deleteMin、deleteMax相同的变换// 要求保证在查找过程中任意当前结点不是2-结点。// 如果被删除的值在树的底部,我们可以直接删除它。// 如果不在,我们需要和二叉查找树中的删除一样:// 用其右子树的最小的值代替该节点的值,并删除那个节点(右子树的最小的值)。// 然后问题已经转化为在一棵根节点的子树中删除最小值,可以使用上面写的删除最小值的方法。func delete(h *Node, key Key) *Node {if key < h.key {// 向左子树搜索,保证左节点不是2-结点if !isRed(h.left) && !isRed(h.left.left) {h = moveRedLeft(h)}h.left = delete(h.left, key)} else {// h.left为红便左旋,使得h.right变红if isRed(h.left) {h = rotateLeft(h)}// 找到目标结点if key == h.key && h.right == nil {// 左子数不为红,且右子树为空,那么左子数只能是空;不然黑节点的高度不平衡return nil}// 无论如何都要删除右子树中的结点//   如果要删的是h,与右子树的min对调后删除右子树的min//   否则目标节点在右子树中// 所以需要让右子结点变红if !isRed(h.right) && !isRed(h.right.right) {h = moveRedRight(h)}// 找到目标节点,将其替换为右子树的最小值// 并删除右子树的最小值if key == h.key {t := findMin(h.right)h.key = t.keyh.val = t.valh.right = delete(h.right, t.key) // 等同于 deleteMin(h.right)} else {h.right = delete(h.right, key)}}return fixUp(h)}func (this *RBT) Delete(key Key) {if !this.Contain(key) {return}this.root = delete(this.root, key)if this.root != nil {this.root.color = BLACK}}

Keys

    func keys(h *Node, res *[]Value) {if h == nil {return}keys(h.left, res)*res = append(*res, h.val)keys(h.right, res)return}func (this *RBT) Keys() []Value {ans := make([]Value, 0)keys(this.root, &ans)return ans}

Contains

    func contrains(h *Node, key Key) *Node {if h == nil {return nil}if key == h.key {return h} else if key < h.key {return contrains(h.left, key)} else {return contrains(h.right, key)}}func (this *RBT) Contains(key Key) bool {return contrains(this.root, key) != nil}

DeleteMin、DeleteMax

    func deleteMax(h *Node) *Node {// h.left是一个红节点,不是h.right的兄弟结点// 需要向左旋转,使得h.left变红,方便从右子树里删除if isRed(h.left) {h = rotateLeft(h)}if h.right == nil {return nil} else if !isRed(h.right) && !isRed(h.right.left) {// h.right 是一个 2-结点// 两种情况:// 1. h.left也是一个2-结点,秩序反转颜色即可// 2. h.left是一个3-结点,需要从h = moveRedRight(h)}h.right = deleteMax(h)return fixUp(h)}func (this *RBT) DeleteMax() {this.root = deleteMax(this.root)if this.root != nil {this.root.color = BLACK}}func deleteMin(h *Node) *Node {if h.left == nil {return nil}// 左子结点是一个2-结点if !isRed(h.left) && !isRed(h.left.left) {// 两种情况:// 1. 右子结点也是一个2-结点,直接颜色翻转即可// 2. 右子结点是一个3-结点,从右子结点借一个给左子结点h = moveRedLeft(h)}h.left = deleteMin(h.left)return fixUp(h)}func (this *RBT) DeleteMin() {this.root = deleteMin(this.root)if this.root != nil {this.root.color = BLACK}return}

Rank、Get

Rank(key Key)返回key的排名,从0开始计数

Get(index int)返回排名为index的key,index从0开始计数

    // 返回排名为rank的结点// rank从0开始func get(h *Node, rank int) *Node {if h == nil || rank == size(h.left) {return h}lsize := size(h.left)if rank > lsize {return get(h.right, rank-lsize-1)} else {return get(h.left, rank)}}func (this *RBT) Get(rank int) *Node {if rank < 0 || rank >= this.Size() {return nil}return get(this.root, rank)}// 返回key的排名,从0开始计数func rank(h *Node, key Key) int {// 返回以h为根节点的子树中小于h.key的键的数量if h == nil {return 0}if key == h.key {return size(h.left)} else if key < h.key {return rank(h.left, key)} else {return 1 + size(h.left) + rank(h.right, key)}}// 红黑树中不存在key返回-1func (this *RBT) Rank(key Key) int {if !this.Contain(key) {return -1}return rank(this.root, key)}

Ceil

    func ceil(h *node, key int) *node {if h == nil {return nil}// h 可能是一个合格的结点,向左尝试找 key <= x < h.keyif h.key >= key {tmp := ceil(h.left, key)if tmp != nil {return tmp}return h} else {return ceil(h.right, key)}}func (this *RBT) Ceil(key int) (bool, int) {n := ceil(this.root, key)if n == nil {return false, -1}return true, n.val}

Floor

    func floor(h *node, key int) *node {if h == nil {return nil}// h 可能是一个合格的结点,也可以向右尝试找 h.key < x <= h.keyif h.key <= key {tmp := floor(h.right, key)if tmp != nil {return tmp}return h} else {return floor(h.left, key)}}func (this *RBT) Floor(key int) (bool, int) {n := ceil(this.root, key)if n == nil {return false, -1}return true, n.val}

例题

  • leetcode 220. 存在重复元素 III:红黑树的添加与删除,实现Ceil(key)找到第一个大于key的数,Floor(key)找到最后一个小于key的数。
  • leetcode 436 寻找右区间:红黑树的添加、Ceil(key)实现;将区间的左端点作为key,下标作为val构建红黑树;多于每一个区间[start,edn]调用Ceil(end)找到第一个大于等于end的左区间端点,并返回其下标即可。

左倾红黑树Go语言实现相关推荐

  1. 左倾红黑树的go语言实现

    简介 红黑树经常能在计算机底层代码中见到,比如 C++的map,multimap, set, multiset Linux中rdtree用以管理内存和进程 Java中的HashMap 左倾红黑树是对红 ...

  2. 红黑树进阶—左倾红黑树(LLBR)介绍

    红黑树已经有很长的历史,在许多现代编程语言的符号表中都有使用,但是其缺点也很明显,其代码实现过于繁杂.因此出现的红黑树的修改版--左倾红黑树 左倾红黑树的代码实现相比于典型的红黑树来说要简单不少,但是 ...

  3. 从2-3树谈到左倾红黑树

    2-3树 定义 顾名思义,2-3树,就是有2个儿子或3个儿子的节点.2-3树就是由这些节点构成.所以2-3-4树的每个节点都是下面中的一个: 空节点:空节点. 2-节点:包含一个元素和两个儿子. 3- ...

  4. 数据结构——左倾红黑树

    左倾红黑树 前提了解 红黑树和平衡多叉树的对应关系 左倾红黑树 基于自顶向下2-3-4树的左倾红黑树 基于2-3树的左倾红黑树 重要代码 左右旋转变色 翻转变色 向2-3左倾红黑树插入 向2-3-4左 ...

  5. 左倾红黑树的原理及简单实现

    (注:以下图片全部源于<算法 第4版>) 左倾红黑树的原理及简单实现 左倾红黑树的简介 左倾红黑树的定义 左倾红黑树与2-3树的对比 左倾红黑树的颜色表示 左倾红黑树的一些基本操作 1.颜 ...

  6. 左倾红黑树(LLRBT)删除操作及相关性质总结答疑

    Left-leaning Red Black Tree 看算法4(算法 第4版 Algorithms 4th Edition)3.4节时,后面的习题有实现左倾红黑树删除操作的代码,刚开始看得云里雾里的 ...

  7. 数据结构之 红黑树(左倾红黑树) java实现

    为啥要有红黑树 上一章我们聊到了二叉查找树数,但是二叉查找树在 插入的时候 如果 递增插入或者递减插入 ,就会导致这个棵树 单边倾斜,或者说单边增长从而退化成链表而影响查询效率,如下图 从而引进了红黑 ...

  8. 左倾红黑树——左倾2-3树(不是jdk1.8的TreeMap的红黑树)

    public class RBTree<K extends Comparable<K>, V> {public static boolean RED = true;public ...

  9. Week 5.1 | 左倾红黑树LLRB | Princeton Algorithms

    文章目录 一.2-3 search trees 1. 定义 2. 操作 3. 复杂度 二.左倾红黑树LLRB 1. 定义 2. 实现 (1)查找 (2)红黑树节点表示 (3)左旋 (4)右旋 (5)翻 ...

最新文章

  1. JS获取一个字符串中被指定的两个字符串包括起来的所有字符串数组
  2. Python天天美味(5) - ljust rjust center
  3. 对于fmri的设计矩阵构造的一个很直观的解释-by 西南大学xulei教授
  4. SOA:A note on RPC
  5. oracle rac 错误日志路径,ORACLE 11G RAC--日志路径
  6. 7.04 -2018-长沙机场笔试总结
  7. 十个改变了计算机世界的算法,你知道几个?
  8. web前端教程之JavaScript的作用域
  9. 计算机组成原理第4章,计算机组成原理-第4章总结.doc
  10. 微软IT规划方法论解读
  11. IOCP 下行为投递的关键点
  12. 〖Python 数据库开发实战 - MySQL篇⑫〗- 数据表的字段约束
  13. 深度学习优化算法之SGD
  14. jmeter使用方法和功能
  15. decent compiled words
  16. MP2315高频同步整流降压x芯片电路原理图
  17. 计算机网络中的广播是指什么,计算机网络中的广播和多播指的是什么?
  18. ChemDraw19激活中文版下载化学绘图软件教程
  19. Axure8有效注册码
  20. python中base函数_详细的python basemap中各函数的所有参量注释

热门文章

  1. 人间简史从动物到上帝读后感_从我的博客到上帝的耳朵...
  2. 查看tmp目录下的文件
  3. The following assertion was thrown building LayoutDemo(dirty): A non-null String must be provided to
  4. 北邮计算机学院国家示范,北京邮电大学获批2020年国家自然科学基金81项
  5. 当阳光照在海面上,当朦胧月色洒在泉水上---我在想你
  6. 烽火HG680-KA-MV300/310-刷机固件及教程
  7. 人生感悟--条条经典
  8. 《FPGA全程进阶---实战演练》第二十一章之 几种常用电平分析及特性
  9. 机器学习分类器——案例(opencv sklearn svm ann)
  10. 图片变色HSV-shader