1. 红黑树的优势

  1. 有了二叉搜索树,为什么还需要平衡二叉树?
    二叉搜索树容易退化成一条链,这时,查找的时间复杂度从 O(log2N)O(log_2N)O(log2​N) 将退化成 O(N)O(N )O(N),引入对左右子树高度差有限制的平衡二叉树,保证查找操作的最坏时间复杂度也为O(log2N)O(log_2N)O(log2​N)
  2. 有了平衡二叉树,为什么还需要红黑树?
    AVL(平衡二叉树)的左右子树高度差不能超过1,每次进行插入/删除操作时,几乎都需要通过旋转操作保持平衡,在频繁进行插入/删除的场景中,频繁的旋转操作使得AVL的性能大打折扣。
    红黑树通过牺牲严格的平衡,换取插入/删除时少量的旋转操作,整体性能优于AVL。红黑树插入时的不平衡,不超过两次旋转就可以解决;删除时的不平衡,不超过三次旋转就能解决。通过红黑树的红黑规则,保证最坏的情况下,也能在O(log2N)O(log_2N)O(log2​N)时间内完成查找操作。

2. 红黑规则

  1. 非黑即红
  2. 根节点为黑色
  3. 叶节点为黑色(叶节点是指末梢的空节点Null)
  4. 一个节点为红色,则其两个子节点必须是黑色的(根到叶子的所有路径,不可能存在两个连续的红色节点)
  5. 每个节点到叶子节点的所有路径,都包含相同数目的黑色节点(相同的黑色高度)

3. 左旋转算法

rotateLeft(TreeNode<K,V> root,TreeNode<K,V> p)

static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,TreeNode<K,V> p) {TreeNode<K,V> r, pp, rl;// 确保 p 的右孩子 r 不为空if (p != null && (r = p.right) != null) {// 将 r 上面的 左孩子 覆盖原来 p 上面的右孩子if ((rl = p.right = r.left) != null)rl.parent = p;// 将 r 作为 头节点 与 p 原来父节点相连,若为null说明作为了根,黑色if ((pp = r.parent = p.parent) == null)(root = r).red = false;else if (pp.left == p)pp.left = r;elsepp.right = r;// p 作为了 r 的左孩子r.left = p;p.parent = r;}return root;
}

4. 右旋转算法

rotateRight(TreeNode<K,V> root,TreeNode<K,V> p)

static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,TreeNode<K,V> p) {TreeNode<K,V> l, pp, lr;// 确保 p 的 左孩子 l 不为nullif (p != null && (l = p.left) != null) {// 将 l 上的 右孩子 覆盖 p 的 左孩子if ((lr = p.left = l.right) != null)lr.parent = p;// 将 l 作为头节点 连接 p 原来的父亲,若为null说明作为了根,黑色if ((pp = l.parent = p.parent) == null)(root = l).red = false;else if (pp.right == p)pp.right = l;elsepp.left = l;// p 作为了 l 的右孩子l.right = p;p.parent = l;}return root;
}

5. 插入平衡算法

规则:

  1. 插入的节点设为红色
  2. 若该节点没有父节点,为根节点,黑色
  3. 若该节点的父亲为黑色或者祖父节点为null,则不用继续调整
  4. 若该节点的父亲为红色并且祖父节点不为空:
    • 判断父节点为祖父的左孩子还是右孩子
    • 若叔节点存在并且为红色,则父、叔节点变为黑色,祖父变为红色,祖父视为新的插入节点向上调整
    • 若叔节点为空或者叔节点为黑色,则根据父节点在祖父的左边还是右边进行旋转,上色规则为叔父都变为黑色,祖父变为红色,以祖父为旋转p点(前后黑色高度不变)

balanceInsertion(TreeNode<K,V> root,TreeNode<K,V> x)

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,TreeNode<K,V> x) {// 新插入的节点设为红色x.red = true;for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {if ((xp = x.parent) == null) {// 如果 x的父节点 为空// 说明 x节点 为根节点-黑色x.red = false;return x;} else if (!xp.red || (xpp = xp.parent) == null)// 如果 x的父节点 为黑色 || x的祖父节点 为空// 说明 不需要再调整,直接返回根节点return root;// x的父节点 为红色 && x的祖父节点 不为空if (xp == (xppl = xpp.left)) {// 如果 x的父节点 为 x的祖父节点 的 左孩子if ((xppr = xpp.right) != null && xppr.red) {// 如果 x的祖父 的 右孩子 不为空 并且为红色// x的祖父 的 左右孩子 都为黑色// x的祖父 为 红色xppr.red = false;xp.red = false;xpp.red = true;// x 变为 x 的祖父-红色x = xpp;} else {// 如果 x的祖父 的 右孩子 为空 或者为黑色if (x == xp.right) {// 如果 x 为父节点的 右孩子// 左旋转root = rotateLeft(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;}if (xp != null) {// 祖父节点为红色,父、叔节点为黑色xp.red = false;if (xpp != null) {xpp.red = true;// 右旋转root = rotateRight(root, xpp);}}}} else {// 如果 x的父节点 为 x的祖父节点 的 右孩子if (xppl != null && xppl.red) {// 如果 x的祖父 的 左孩子 不为空 并且为红色xppl.red = false;xp.red = false;xpp.red = true;x = xpp;} else {// 如果 x的祖父 的 左孩子 为空 或者为黑色if (x == xp.left) {// 如果 x 为父节点的 左孩子// 右旋转root = rotateRight(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;}if (xp != null) {// 祖父节点为红色,父、叔节点为黑色xp.red = false;if (xpp != null) {xpp.red = true;// 左旋转root = rotateLeft(root, xpp);}}}}}
}
将代码进行拆解图示说明

x的父节点 为 x的祖父节点 的 左孩子:

  1. 如果 x的祖父 的 右孩子 不为空 并且为红色

    if ((xppr = xpp.right) != null && xppr.red) {// x的祖父 的 左右孩子 都为黑色// x的祖父 为 红色xppr.red = false;xp.red = false;xpp.red = true;// x 变为 x 的祖父-红色x = xpp;
    }
    

    变色:

  2. 如果 x的祖父 的 右孩子 为空 或者为黑色

    1. 如果 x 为父节点的 右孩子

      if (x == xp.right) {// 左旋转root = rotateLeft(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;
      }
      

      左旋转(父节点 p):

    2. 如果 x 为父节点的 左孩子

      if (xp != null) {// 祖父节点为红色,父、叔节点为黑色xp.red = false;if (xpp != null) {xpp.red = true;// 右旋转root = rotateRight(root, xpp);}
      }
      

      变色:
      右旋转(祖父节点 p):

x的父节点 为 x的祖父节点 的 右孩子:

  1. 如果 x的祖父 的 左孩子 不为空 并且为红色

    if (xppl != null && xppl.red) {xppl.red = false;xp.red = false;xpp.red = true;x = xpp;
    }
    

    变色:

  2. 如果 x的祖父 的 左孩子 为空 或者为黑色

    1. 如果 x 为父节点的 左孩子

      if (x == xp.left) {// 右旋转root = rotateRight(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;
      }
      

      右旋转(父节点 p):

    2. 如果 x 为父节点的 右孩子
      if (xp != null) {// 祖父节点为红色,父、叔节点为黑色xp.red = false;if (xpp != null) {xpp.red = true;// 左旋转root = rotateLeft(root, xpp);}}
      

      变色:
      左旋转(祖父节点 p):

Java之HashMap经典算法-红黑树(插入节点平衡调整,左旋转,右旋转)相关推荐

  1. 底层实现红黑树_stl map底层之红黑树插入步骤详解与代码实现 | 学步园

    本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进行调整的过程进行的解释. 最近在看stl源码剖析,看到map底层红黑树的实现.为了加深对于红黑树的理解就自己动手写了红 ...

  2. 数据结构-红黑树插入结点示例

    数据结构-红黑树插入结点示例 1.红黑树简介 2.在线可视化生成红黑树工具 3.红黑树插入结点性质和规则 3.1.红黑树插入结点性质 3.2.红黑树插入结点规则 4.红黑树插入结点示例 4.1.红黑树 ...

  3. 红黑树的原理_红黑树插入算法实现原理分析

    ­ 引言 红黑树是在实际工程中被广泛应用的一种数据结构,比如Linux中的线程调度就是使用的红黑树来管理进程控制块,而Nginx中也是使用红黑树来管理的timer,Java中的TreeMap和Tree ...

  4. 【算法】红黑树插入数据(变色,左旋、右旋)(二)

    本人菜鸡一只,正在更新红黑树系列的文章. 该系列已经全部更完,有5篇文章: [算法]红黑树(二叉树)概念与查询(一):https://blog.csdn.net/lsr40/article/detai ...

  5. 红黑树 插入算法(一)

    前言 红黑树在数据结构里面,是一种能自动平衡的树,它的查询速度很快,因为能够用到二分法,二分法的查询复杂度只有O(log2(N)),几万条的数据也就只需查十几次,不过要维持那么高的查询速度也是有代价, ...

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

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

  7. 遍历HashMap源码——红黑树原理、HashMap红黑树实现与反树型化(三)

    本章将是HashMap源码的最后一章,将介绍红黑树及其实现,HashMap的remove方法与反树型化.长文预警~~ 遍历HashMap源码--红黑树原理.HashMap红黑树实现与反树型化 什么是红 ...

  8. 花开--HashMap系列之红黑树篇(二)

    昨天小宋把HashMap的基本理论和1.7底层做了着重的分析讲解,今天小宋接着上篇文章最后提到过的红黑树继续往下讲. 文章目录 红黑树 二叉查找树简介 左旋和右旋(平衡二叉查找树) 红黑树简介 红黑树 ...

  9. 红黑树----红黑树插入和删除结点的全程演示

    引言: 目前国内图书市场上,抑或网上讲解红黑树的资料层次不齐,混乱不清,没有一个完整而统一的阐述.而本人的红黑树系列四篇文章(详见文末的参考文献),虽然从头至尾,讲的有根有据,层次清晰,然距离读者真正 ...

最新文章

  1. 万维网与HTTP协议
  2. 微服务~分布式事务里的最终一致性
  3. python爬取慕课视频-Python爬虫抓取技术的门道
  4. 最新版elasticsearch的安装踩坑
  5. 【.NET Core项目实战-统一认证平台】第十三章 授权篇-如何强制有效令牌过期
  6. 服务器c盘windows文件夹太大,Win10C盘windows文件夹过大怎么办?Win10C盘windows文件夹过大的解决方法...
  7. (())、let、expr、bc等计算命令的使用语法和应用技巧
  8. html页面简单访问限制
  9. SQL在线格式化工具
  10. 谷歌地图网页版_如何在网站嵌入谷歌地图
  11. html文件如何显示大纲视图,如何使用大纲视图生成章节目录
  12. C#验证是不是合法的18位身份证号码
  13. 饭菜先生推出首款可随心定制的电子菜谱
  14. malloc,calloc区别
  15. iOS15.4 Beta4 新测试版推送,新增反跟踪功能
  16. Android仿微信通讯录
  17. 直播弹幕互动游戏如何开播?
  18. Matlab quiver函数用法 - 画矢量箭头图
  19. 小论文格式要求(2010年版)
  20. MySQL千万级数据量优化方案

热门文章

  1. 失业日记 10月8日
  2. 笔记本硬盘飚速 首款1T 7200转本盘详测
  3. 怎么屏蔽还有照片_“朋友,别逼我屏蔽你”:乱象丛生的朋友圈,到此为止吧...
  4. session,sessionid,cookie之间的关系解析
  5. SpringBoot做的两个系统,一个时间定时任务(quartz),一个微信签到(附源码)
  6. RoboMasterAI挑战赛-装甲板识别与测距 jetsonNX+realsense
  7. 零散小知识(Website Recommend)
  8. decaf 接口用法
  9. H5页面点击调起腾讯/百度/高德地图APP
  10. GeoServer问题集锦