左倾红黑树

  • 前提了解
  • 红黑树和平衡多叉树的对应关系
  • 左倾红黑树
  • 基于自顶向下2-3-4树的左倾红黑树
  • 基于2-3树的左倾红黑树
  • 重要代码
    • 左右旋转变色
    • 翻转变色
  • 向2-3左倾红黑树插入
  • 向2-3-4左倾红黑树插入的额外情况
  • 插入代码
  • 2-3左倾红黑树删除最小值的情况
  • 2-3-4左倾红黑树删除最小值的额外情况
  • 2-3左倾红黑树删除最大值的情况
  • 2-3-4左倾红黑树删除最大值的额外情况
  • 2-3和2-3-4左倾红黑树删除一般情况
  • 2-3左倾红黑树完整代码
  • 2-3-4左倾红黑树完整代码

前提了解

  • B树、2-3树、2-3-4树
  • 二叉搜索树、平衡二叉树
  • Robert Sedgewick原论文
  • Robert Sedgewick论文翻译和解释

红黑树和平衡多叉树的对应关系

  • 黑链接相当于2-节点
  • 一边红链接相当于3-节点
  • 两边红链接相当于4-节点
  • 红链接与红节点是等效的(链接是节点的内部属性,表示父节点对其的指向)

左倾红黑树

根据Robert Sedgewick原论文的定义:左倾红黑树指3-节点向左倾斜的红黑树,其具有2种形式

  • 基于自顶向下2-3-4树的左倾红黑树
  • 基于2-3树的左倾红黑树

具有以下特性

  • 不能出现向右倾斜的红链接
  • 从根到叶节点不包含两条连续的红色链接
  • 从根到叶节点的每条路径拥有相同的黑色链接数量

基于自顶向下2-3-4树的左倾红黑树

向左倾斜的要求决定了红黑树和 2-3-4 树之间一对一的对应关系,从而减少了要考虑的情况

自底向上:指插入4-叶子节点后开始分裂,并将中间值传给父节点,如果其父节点也是4-节点,继续向上分裂,直到到达根节点,如果根节点也是4-节点,分裂后树的高度+1,如下图

自顶向下:从根节点到插入所在的叶子节点路径上,遇到4-节点就将其分裂,这样确保了最后到达的叶子节点必然是2-或者3-节点,搜索路径上不存在4-节点,如下图

基于2-3树的左倾红黑树

向左倾斜的要求决定了红黑树和 2-3 树之间一对一的对应关系,从而减少了要考虑的情况

重要代码

下面代码图片来源于原论文

左右旋转变色

出现向右倾斜的红链接或两个连续的红链接时需要旋转变色

翻转变色

左右都是红链接时,需要翻转变色,若翻转到根节点,则黑链高度增加

  • 对于2-3左倾红黑树来说,相当于分裂生成的4-节点
  • 对于2-3-4左倾红黑树来说,相当于分裂遇到的4-节点

向2-3左倾红黑树插入

向2-节点插入,插入左边直接插入,插入右边需左旋(因为不能出现向右倾斜的红链接),相当于将2-节点变成了3-节点

向3-节点插入,插入左中位置,需要旋转调整(因为不能两条连续的红色链接),此时左右两边都有红链接,已无法通过旋转调整,相当于将3-节点变成了4-节点,然后通过翻转变色分裂生成的4-节点

可以看到是先旋转变色调整,再判断是否需要翻转变色

向2-3-4左倾红黑树插入的额外情况

向2节点插入同2-3树,但向3-节点插入,3-节点变成4-节点,即两边红链

如果出现4-节点,下次插入搜索时将拆分4-节点(就不会出现向4-节点添加元素的情况),当其父节点为2-节点时,根据自顶向下的规则,分裂遇到的4-节点,传递中间值给父节点,父节点变成3-节点,问题转为插入2-节点,即先翻转变色再判断是否需要旋转变色

如下同理,只不过4-节点的父节点是3-节点的情况

插入代码

根据上面对2-3左倾红黑树和2-3-4左倾红黑树的分析,作者给出了插入的代码

将两边红链接翻转的那行代码放到前面是自顶向下的2-3-4左倾红黑树,放到后面是2-3左倾红黑树

2-3左倾红黑树删除最小值的情况

如下在2-3树删除最小值中,只有当待删除最小值所在节点为3-叶节点或其父节点为3-节点时不影响树高

  • 前者直接删除,如下图
  • 后者能将3-节点重新分配或合并成2-节点,如下图
    (其实还有一种情况是待删除最小值所在节点的兄弟节点是3-节点时,如下图,但这在左倾红黑树中不存在)

    对应到红黑树,删除不影响黑链数量,需要保证待删除最小值的节点为红链接或其父节点为红链接其对立面是待删除最小值节点和父节点都是黑链接,为了消除这个情况,原论文采用了一个非常极端的方法,将左搜索路径的连续黑链接都翻转变色,即代码中的 if (!isRed(h.left) && !isRed(h.left.left))及flipsColors()

    flipsColors()的操作会导致:
  • 引入连续的左左红色链接(如插入60-50-70-40-55-80-90-30,删除30,会导致50和40红)
  • 引入左右红色链接(如插入60-50-70-40-55-80-90,删除40,会导致50和70红)
  • 引入单右红色链接(如插入60-50-70,删除50,会导致70红)
  • 引入左右红色链接导致的连续右左红色链接(如插入60-50-70-40-55-80-90-53,删除40,会导致55和53红)

前三种情况刚好对应2-3树插入时的修正代码,即fixup(),可在递归回调时修正

最后一种情况遇到时应该立马修正,即代码中的 isRed(h.right.left),原论文中给了示例,如下图

2-3-4左倾红黑树删除最小值的额外情况

细心的人可能发现,在2-3-4左倾红黑树可能多出现另一种情况,这在原论文没有给出,即右边左右红色链接,这需要更多的旋转操作

2-3左倾红黑树删除最大值的情况

同理,只有当待删除最大值为3-叶节点或其父节点为3-节点时不影响树高,对应到红黑树需要保证待删除最大值的节点为红链接或其父节点为红链接才不影响黑链数量,其对立面是待删除最大值节点和父节点都是黑链接

可按照删除最小值的方式将右搜索路径的连续黑链接都翻转变色,但当其左链接为红色时,其本身就是3-节点,需要对其右旋恢复,故只有当其左连接不为红才需要翻转,即代码中的!isRed(h.right) && !isRed(h.right.left)和flipColors()

同理flipsColors()的操作会导致:

  • 引入单左红色链接(如插入60-50-70,删除70,会导致50红)
  • 引入左右红色链接(如插入60-50-70-40-55-80-90,删除90,会导致50和70红)
  • 引入左右红色链接导致的右右红色连续链接(如插入60-50-70-40-55-80-90-100,删除100,会导致70和90红)
  • 引入左右红色链接导致的左左红色连续链接(如插入60-50-70-40-55-80-90-75,删除90,会导致80和75红)

第一种情况不用管,第二三种情况将在fixUp()中修复,第四种情况在遇到后要立即修复,即代码中的isRed(h.left.left)

2-3-4左倾红黑树删除最大值的额外情况

在2-3-4左倾红黑树可能多出现另一种情况,即左边为左右红色链接,如下图,其变换步骤一样,不需要额外的代码

2-3和2-3-4左倾红黑树删除一般情况

要理解删除一般情况的代码,必须充分理解删除最大最小值的代码,尤其是为什么不能出现连续的2-节点,关于这点,可以再看看这篇 https://www.freesion.com/article/4026432302/#Key_398

2-3左倾红黑树完整代码

原论文代码是运行不了的,为了测试对原论文进行了一些修改,但关键代码是一样的

class LL23RB<Key extends Comparable<Key>, Value> {private static final boolean RED = true;private static final boolean BLACK = false;private Node root;private class Node {private Key key;private Value value;private Node left, right;private boolean color;Node(Key key, Value value) {this.key = key;this.value = value;this.color = RED;}}public Value search(Key key) {Node searchNode = searchNode(root, key);if (searchNode != null) {return searchNode.value;}return null;}private Node searchNode(Node node, Key key) {Node x = node;while (x != null) {int cmp = key.compareTo(x.key);if (cmp == 0) return x;else if (cmp < 0) x = x.left;else if (cmp > 0) x = x.right;}return null;}public void insert(Key key, Value value) {root = insert(root, key, value);root.color = BLACK;}private Node insert(Node node, Key key, Value value) {if (node == null) return new Node(key, value);int cmp = key.compareTo(node.key);if (cmp == 0) node.value = value;else if (cmp < 0) node.left = insert(node.left, key, value);else node.right = insert(node.right, key, value);node = fixUp(node);return node;}private boolean isRed(Node node) {if (node == null) {return false;}return node.color == RED;}private Node fixUp(Node node) {if (isRed(node.right) && !isRed(node.left)) node = rotateLeft(node);if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node);if (isRed(node.left) && isRed(node.right)) flipColors(node);return node;}private Node rotateLeft(Node h) {Node x = h.right;h.right = x.left;x.left = h;x.color = h.color;h.color = RED;return x;}private Node rotateRight(Node h) {Node x = h.left;h.left = x.right;x.right = h;x.color = h.color;h.color = RED;return x;}private void flipColors(Node h) {h.color = !h.color;h.left.color = !h.left.color;h.right.color = !h.right.color;}public void deleteMin() {root = deleteMin(root);root.color = BLACK;}private Node deleteMin(Node h) {if (h.left == null) return null;if (!isRed(h.left) && !isRed(h.left.left))h = moveRedLeft(h);h.left = deleteMin(h.left);return fixUp(h);}private Node moveRedLeft(Node h) {flipColors(h);if (isRed(h.right.left)) {h.right = rotateRight(h.right);h = rotateLeft(h);flipColors(h);}return h;}public void deleteMax() {root = deleteMax(root);root.color = BLACK;}private Node deleteMax(Node h) {if (isRed(h.left)) {h = rotateRight(h);}if (h.right == null) {return null;}if (!isRed(h.right) && !isRed(h.right.left)) {h = moveRedRight(h);}h.right = deleteMax(h.right);return fixUp(h);}private Node moveRedRight(Node h) {flipColors(h);if (isRed(h.left.left)) {h = rotateRight(h);flipColors(h);}return h;}public void delete(Key key) {root = delete(root, key);root.color = BLACK;}private Node min(Node node) {if (node == null) {return null;}Node current = node;while (current.left != null) {current = current.left;}return current;}private Value get(Node node, Key key) {Node searchNode = searchNode(node, key);if (searchNode != null) {return searchNode.value;}return null;}private Node delete(Node h, Key key) {if (key.compareTo(h.key) < 0) {if (!isRed(h.left) && !isRed(h.left.left))h = moveRedLeft(h);h.left = delete(h.left, key);} else {if (isRed(h.left))h = rotateRight(h);if (key.compareTo(h.key) == 0 && (h.right == null))return null;if (!isRed(h.right) && !isRed(h.right.left))h = moveRedRight(h);if (key.compareTo(h.key) == 0) {h.value = get(h.right, min(h.right).key);h.key = min(h.right).key;h.right = deleteMin(h.right);} else h.right = delete(h.right, key);}return fixUp(h);}private void innerMidOrderTraversal(Node node) {if (node == null) {return;}innerMidOrderTraversal(node.left);System.out.print("[" + node.key + "-" + node.value + "]");if (node.color) {System.out.print("r" + " ");} else {System.out.print("b" + " ");}innerMidOrderTraversal(node.right);}@NonNull@Overridepublic String toString() {System.out.print("中序遍历: ");innerMidOrderTraversal(root);return "";}
}

2-3-4左倾红黑树完整代码

相比于2-3左倾红黑树代码,修改了insert()、moveRedLeft()部分

class LL234RB<Key extends Comparable<Key>, Value> {private static final boolean RED = true;private static final boolean BLACK = false;private Node root;private class Node {private Key key;private Value value;private Node left, right;private boolean color;Node(Key key, Value value) {this.key = key;this.value = value;this.color = RED;}}public Value search(Key key) {Node searchNode = searchNode(root, key);if (searchNode != null) {return searchNode.value;}return null;}private Node searchNode(Node node, Key key) {Node x = node;while (x != null) {int cmp = key.compareTo(x.key);if (cmp == 0) return x;else if (cmp < 0) x = x.left;else if (cmp > 0) x = x.right;}return null;}public void insert(Key key, Value value) {root = insert(root, key, value);root.color = BLACK;}private Node insert(Node h, Key key, Value value) {if (h == null) return new Node(key, value);if (isRed(h.left) && isRed(h.right)) flipColors(h);int cmp = key.compareTo(h.key);if (cmp == 0) h.value = value;else if (cmp < 0) h.left = insert(h.left, key, value);else h.right = insert(h.right, key, value);if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);return h;}private Node rotateLeft(Node h) {Node x = h.right;h.right = x.left;x.left = h;x.color = h.color;h.color = RED;return x;}private Node rotateRight(Node h) {Node x = h.left;h.left = x.right;x.right = h;x.color = h.color;h.color = RED;return x;}private void flipColors(Node h) {h.color = !h.color;h.left.color = !h.left.color;h.right.color = !h.right.color;}private boolean isRed(Node node) {if (node == null) {return false;}return node.color == RED;}public void deleteMin() {root = deleteMin(root);root.color = BLACK;}private Node deleteMin(Node h) {if (h.left == null) return null;if (!isRed(h.left) && !isRed(h.left.left))h = moveRedLeft(h);h.left = deleteMin(h.left);return fixUp(h);}private Node moveRedLeft(Node h) {flipColors(h);if (isRed(h.right.left)) {if (isRed(h.right.right)) {//这是2-3-4树额外的情况,不能复用代码,因为旋转时修改了hh.right = rotateRight(h.right);h = rotateLeft(h);h = rotateLeft(h);h.left = rotateRight(h.left);flipColors(h);} else {h.right = rotateRight(h.right);h = rotateLeft(h);flipColors(h);}}return h;}private Node moveRedRight(Node h) {flipColors(h);if (isRed(h.left.left)) {h = rotateRight(h);flipColors(h);}return h;}public void deleteMax() {root = deleteMax(root);root.color = BLACK;}private Node deleteMax(Node h) {if (isRed(h.left)) {h = rotateRight(h);}if (h.right == null) {return null;}if (!isRed(h.right) && !isRed(h.right.left)) {h = moveRedRight(h);}h.right = deleteMax(h.right);return fixUp(h);}private Node fixUp(Node node) {if (isRed(node.right) && !isRed(node.left)) node = rotateLeft(node);if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node);if (isRed(node.left) && isRed(node.right)) flipColors(node);return node;}public void delete(Key key) {root = delete(root, key);root.color = BLACK;}private Node min(Node node) {if (node == null) {return null;}Node current = node;while (current.left != null) {current = current.left;}return current;}private Value get(Node node, Key key) {Node searchNode = searchNode(node, key);if (searchNode != null) {return searchNode.value;}return null;}private Node delete(Node h, Key key) {if (key.compareTo(h.key) < 0) { //处理向左搜索路径的情况,避免连续的2-节点if (!isRed(h.left) && !isRed(h.left.left))h = moveRedLeft(h);h.left = delete(h.left, key);       //向左走} else {            //处理向右和相等的情况if (isRed(h.left))             //若当前是3-节点,则先右旋恢复h = rotateRight(h);if (key.compareTo(h.key) == 0 && (h.right == null)) //处理删除根节点或叶节点的情况,如60-50删除60return null;if (!isRed(h.right) && !isRed(h.right.left))    //处理向右搜索路径的情况,避免连续的2-节点h = moveRedRight(h);if (key.compareTo(h.key) == 0) {        //处理删除的是内部节点的情况,用后继节点键值替代,转为删除后继节点h.value = get(h.right, min(h.right).key);h.key = min(h.right).key;h.right = deleteMin(h.right);} else h.right = delete(h.right, key);     //向右走}return fixUp(h);    //递归回调时消除生成的右红链接和左右红链接}private void innerMidOrderTraversal(Node node) {if (node == null) {return;}innerMidOrderTraversal(node.left);System.out.print("[" + node.key + "-" + node.value + "]");if (node.color) {System.out.print("r" + " ");} else {System.out.print("b" + " ");}innerMidOrderTraversal(node.right);}@NonNull@Overridepublic String toString() {System.out.print("中序遍历: ");innerMidOrderTraversal(root);return "";}
}

数据结构——左倾红黑树相关推荐

  1. Robert Sedgewick左倾红黑树论文翻译

    原论文 Robert Sedgewick原论文--Left-leaning Red-Black Trees 本人翻译 为保持一致性,下面将贴截图,未翻译后面的性能分析部分(水平有限,可能有些地方不够准 ...

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

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

  3. 左倾红黑树Go语言实现

    文章目录 左倾红黑树的定义 红黑树性质 Node数据结构 旋转 插入 颜色转换 删除 实现 Keys Contains DeleteMin.DeleteMax Rank.Get Ceil Floor ...

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

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

  5. 从二叉查找树到平衡树:avl, 2-3树,左倾红黑树(含实现代码),传统红黑树...

    参考:自平衡二叉查找树 ,红黑树, 算法:理解红黑树 (英文pdf:红黑树) 目录 自平衡二叉树介绍 avl树 2-3树 LLRBT(Left-leaning red-black tree左倾红黑树 ...

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

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

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

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

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

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

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

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

最新文章

  1. C++_泛型编程与标准库(五)
  2. 多任务学习,如何设计一个更好的参数共享机制?| AAAI 2020
  3. Git使用汇总之暂存区工作区撤销和删除
  4. 最小二乘法least square
  5. js处理时间的那些事
  6. redis雪崩解决方案
  7. IBM HTTP Server Websphere Plugin
  8. OpenCV学习笔记:矩阵的掩码操作
  9. 在线图表编辑工具 draw.io 10.6.5 版本发布
  10. 程序员应该学的追女孩方法
  11. 流量卡物联网卡管理平台源码|PHP管理系统源码
  12. 图床上传系统设计分析
  13. java webservice报文过长_年薪百万IT大牛分享及(京东,阿里,58)Java初中高级765道面试题...
  14. fedora20配置静态ip
  15. 高颜值的第三方网易云播放器YesPlayMusic
  16. 计算机无线网络设备有哪些,电脑无线上网设备有哪几种
  17. 屏蔽iOS11 Xcode9下打印的乱七八糟的日志
  18. 【图像处理】SFR算法详解1
  19. BP神经网络逼近函数项目(含matlab代码)
  20. 学画画软件app推荐_最好用的绘画软件APP,学画画必备

热门文章

  1. xp系统修复 sfc /scannow 免光盘技巧
  2. 图片饱和度更改,c#,winform
  3. 软件测试的魅力何在?您为什么选择测试一行而不做开发?
  4. 阴阳师各服务器在线人数,阴阳师伪神活动成难忘今宵,玩家团结一心,你换了哪个SSR?...
  5. 题目 - 北京地铁 描述 北京地铁涨价了,现在的地铁票价计算方法如下: 6公里内 3元 (包括6公里) 6-12 公里 4元 (不包括6公里,包括12公里) 12-32 公里 每10公里加1元
  6. c语言创建空顺序表的程序,C语言实现顺序表的创建与增删改查操作
  7. python跑酷游戏源码_HTML5游戏实战(1):50行代码实现正面跑酷游戏
  8. Android Amazon S3 功能接入
  9. Vue 过滤器、计算属性、侦听器 图解版 一目了然
  10. ubuntu 终端显示英文,桌面环境显示中文方法