【数据结构与算法】手撕红黑树

Python微信订餐小程序课程视频https://edu.csdn.net/course/detail/36074Python实战量化交易理财系统https://edu.csdn.net/course/detail/35475红黑树定义动机:二叉查找树查找、插入、删除最坏情况时间复杂度可能退化为 O(n)。AVL 树很好的限制了数的高度为 O(logn),插入、删除、查找的最坏时间复杂度均为 O(logn);但删除操作最多需要做 O(logn) 次旋转。红黑树是具有如下特点的二
摘要由CSDN通过智能技术生成

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475

红黑树

定义

动机

  • 二叉查找树查找、插入、删除最坏情况时间复杂度可能退化为 O(n)

  • AVL 树很好的限制了数的高度为 O(logn),插入、删除、查找的最坏时间复杂度均为 O(logn);但删除操作最多需要做 O(logn) 次旋转。

  • 红黑树是具有如下特点的二叉查找树:

    • 每个结点是红色或黑色的
    • 根结点为黑色
    • 外结点为黑色(外界点即为 null
    • 如果一个结点时红色,那么它的孩子必须是黑色
    • 任一结点到外结点的路径上,包含相同数目的黑结点


结点的黑高度:该结点到外结点的路径上包含的黑结点数目

红黑树的黑高度:根结点的黑高度

性质

  • 若忽略红结点而只考虑黑结点,则这棵树是平衡的
  • 任何一条路径上不能有两个连续的红结点。从任意结点触发最长的路径(红黑结点间隔组成)是最短路径(仅由黑结点组成)的 2
  • 任何一个结点的左右子树的高度最多相差 2
  • 红黑树的平衡性比 AVL 树更弱
  • 平均和最坏高度:O(logn)
  • 查找、插入、删除操作的平均和最坏时间复杂度是 O(logn),且仅涉及 O(1) 次旋转
  • 红黑树的高度:h = O(logn)logn <= h <= 2logn

结点定义


1️⃣ key:关键字的值
2️⃣ value:关键字的存储信息
3️⃣ parent:父亲结点的引用
4️⃣ left:左子树根结点的引用
5️⃣ right:右子树根结点引用
6️⃣ color:结点颜色

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

java`class RBNodeextends Comparable, V> {
public RBNode parent;
public RBNode left;
public RBNode right;
public boolean color;
public K key;
public V value;

public RBNode(RBNode parent, K key, V value) {
this.parent = parent;
this.key = key;
this.value = value;
}
}`

插入算法

  • 查找,若查找成功则不插入并更新结点值;若查找失败,再查找失败的位置插入新结点
  • 新结点总是作为叶结点插入的
  • 新结点必须为红色
  • 若新结点的父结点是黑色,则插入过程结束
  • 若新结点的父结点是红色,则需要处理双红缺陷

这里定义 x 为新插入的结点,px 的父结点,gx 的爷爷结点,ux 的叔叔

双红修正

1️⃣ x 的叔叔是黑色

  • gx 的路径为 LL
  • gx 的路径为 RR
  • gx 的路径为 LR
  • gx 的路径为 RL

2️⃣ x 的叔叔是红色

实例


此时结点 50 的父亲结点 60 也为红色,需要进行双红修正。注意到,其叔叔结点 20 也是红色,只需要将父亲结点 60 和叔叔结点 20 变为黑色,再把爷爷结点 30 变为红色,结果如下图。


之后会发现,结点 30 和其父结点 70 都为红色,需要进行双红修正。而结点 30 的叔叔结点 85 为黑色,结点 30 的爷爷结点 15 到达结点 30 的路径是 RL 型,故需要先右旋,再左旋。结果如下图。

旋转操作

由于红黑树结点还拥有 parent 属性,故不能像平衡二叉树一样进行旋转(【数据结构与算法】手撕平衡二叉树),需要特殊考虑 parent的赋值。

1️⃣ RR

左旋


需要特别注意的是爷爷结点 gparent 的设置,需要先判断 g 是左子结点还是右子结点。

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

java private void leftRotate(RBNode g) { if (g != null) { RBNode p = g.right; g.right = p.left; if (p.left != null) { p.left.parent = g; } p.parent = g.parent; if (g.parent == null) { this.root = p; } else if (g.parent.left == g) { g.parent.left = p; } else { g.parent.right = p; } p.left = g; g.parent = p; }
2️⃣ LL

右旋


需要特别注意的是爷爷结点 gparent 的设置,需要先判断 g 是左子结点还是右子结点。

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

java private void rightRotate(RBNode g) { if(g != null) { RBNode p = g.left; g.left = p.right; if(p.right != null) { p.right.parent = g; } p.parent = g.parent; if(g.parent == null) { this.root = p; } else if(g.parent.left == g) { g.parent.left = p; } else { g.parent.right = p; } p.right = g; g.parent = p; } }
3️⃣ LR

先对 g 的左子结点左旋,再对 g 右旋

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

java private void leftRightRotate(RBNode g) { if(g != null) { leftRotate(g.left); rightRotate(g); } }
4️⃣ RL

先对 g 的右子结点右旋,再对 g 左旋

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

java private void rightLeftRotate(RBNode g) { if(g != null) { rightRotate(g.right); leftRotate(g); } }

代码

1️⃣ 插入操作

代码描述

  • 如果根结点为空,则创建根结点,返回
  • 否则,根据 key 与结点关键值的比较找到插入位置</
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值