美团实习面试:熟悉红黑树是吧?能不能写一下?
程序员的成长之路
共 3450字,需浏览 7分钟
·
2021-05-29 13:26
阅读本文大概需要 6 分钟。
来自:https://zhenbianshu.github.io/
红黑树的插入和删除非常复杂,很多人并没有理解或完全实现,或实现了的没有任何注释,让人很难参考; 网络上红黑树的理解方式较为单一,一般是 双黑、caseN
法,而插入和删除的情况很多,每种都有对应的处理方式,如果死记硬背的话,再过一段时间再回忆各种情况可能就一头雾水了。
红黑树
节点是红色或黑色。 根是黑色。 所有叶子都是黑色(叶子是NIL节点)。 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。) 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
优势和用途
O(n)
。2log(n+1)
,查找效率最坏为 O(log(n))
。2-3-4树
所有叶子节点都拥有相同的深度。
节点只能是 2-节点、3-节点、4-节点之一。
2-节点:包含 1 个元素的节点,有 2 个子节点; 3-节点:包含 2 个元素的节点,有 3 个子节点; 4-节点:包含 3 个元素的节点,有 4 个子节点; 元素始终保持排序顺序,整体上保持二叉查找树的性质,即父结点大于左子结点,小于右子结点;而且结点有多个元素时,每个元素必须大于它左边的和它的左子树中元素。
对应红黑树
把红黑树从根结点到叶子结点的黑色结点个数定义为高度
。结点插入
插入都是向最下面一层插入; 升元:将插入结点由 2-结点升级成 3-结点,或由 3-结点升级成 4-结点; 向 4-结点插入元素后,需要将中间元素提到父结点升元,原结点变成两个 2-结点,再把元素插入 2-结点中,如果父结点也是 4-结点,则递归向上层升元,至到根结点后将树高加1;
新插入的结点颜色为 红色
,这样才可能不会对红黑树的高度产生影响。2-结点对应红黑树中的单个黑色结点,插入时直接成功(对应 2-结点升元)。 3-结点对应红黑树中的 黑+红
子树,插入后将其修复成红+黑+红
子树(对应 3-结点升元);4-结点对应红黑树中的 红+黑+红
子树,插入后将其修复成红色祖父+黑色父叔+红色孩子
子树,然后再把祖父结点当成新插入的红色结点递归向上层修复,直至修复成功或遇到 root 结点;
删除结点
查找最近的叶子结点中的元素替代被删除元素,删除替代元素后,从替代元素所处叶子结点开始处理; 降元:4-结点变 3-结点,3-结点变 2-结点; 2-结点中只有一个元素,所以借兄弟结点中的元素来补充删除后的造成的空结点; 当兄弟结点中也没有多个元素可以补充时,尝试将父结点降元,失败时向上递归,至到子树降元成功或到 root 结点树高减1;
查找离当前结点最近的叶子结点作为
替代结点
(左子树的最右结点或右子树的最左结点都能保证替换后保证二叉查找树的结点的排序性质,叶子结点的替代结点是自身)替换掉被删除结点,从替代的叶子结点向上递归修复;替代结点颜色为红色(对应 2-3-4树中 4-结点或 3-结点)时删除子结点直接成功;
替代结点为黑色(对应 2-3-4树中 2-结点)时,意味着替代结点所在的子树会降一层,需要依次检验以下三项,以恢复子树高度:
兄弟结点的子结点中有红色结点(兄弟结点对应 3-结点或 4-结点)能够“借用”,旋转过来后修正颜色; 父结点是红色结点(父结点对应 3-结点或 4-结点,可以降元)时,将父结点变黑色,自身和兄弟结点变红色后删除; 父结点和兄弟结点都是黑色时,将子树降一层后把 父结点当作替代结点
递归向上处理。
找到替代结点
,如果替代结点是黑色,递归向上依次判断侄子结点、父结点是否可以补充被删除的黑色,整体思想就是将删除一个黑色结点造成的影响局限在子树内处理。小结
由于插入多个结点时,无法确定是处理哪个结点时出了问题,于是我给红黑树类添加了 debug
属性,用二分法设置此属性来找到问题结点;给红黑树类添加了 printTree()
方法,实时打印树结构,确定代码问题再分析;
旋转
就包含了 左旋/右旋/先左旋再右旋/先右旋再左旋
几种情况,虽然有一定规律,还是自己实现一下印象最深刻。推荐阅读:
快来试试 Spring Boot 应用可视化监控,一目了然!
微信扫描二维码,关注我的公众号
朕已阅
评论