最终还是决定把红黑树的篇章一分为二,插入操作一篇,删除操作一篇,因为合在一起写篇幅实在太长了,写起来都觉得累,何况是阅读并理解的读者。
红黑树删除操作请参考 数据结构 - 红黑树(Red Black Tree)删除详解与实现(Java)
现在网络上最不缺的就是对某个知识点的讲解博文,各种花样标题百出,更有类似“一文讲懂xxx”,“史上最简单的xxx讲解”,“xxx看了还不懂你打我”之类云云。其中也不乏有些理论甚至是举例都雷同的两篇不同文章,至于作者是不是真的理解自己所写的内容暂且不说,技术博客这种东西,本来就是提供给大家分享自己学习体会的一个平台,我也不敢说自己写的就足够全面简洁易懂,只能说有些东西确实不是一两篇文章就能理解透彻的,只有多读,多思考,慢慢的就会明了,我也是读了好几个人的博文才读懂的,一些前辈的文章确实很不错,值得参考和学习。仅希望我所写这两篇关于红黑树的文章能在众多的同类博文中给偶然看到的读者一点点启示。
正文。
本文要求懂得二叉搜索树的原理,如果还不理解可以转阅(理解第一篇便可以):
一、数据结构 - 从二叉搜索树说到AVL树(一)之二叉搜索树的操作与详解(Java)
二、数据结构 - 从二叉搜索树说到AVL树(二)之AVL树的操作与详解(Java)
众所皆知,二叉平衡树(Binary Balanced Tree)的出现是为了让一棵二叉搜索树的查找效率尽可能的最大化,同时为了构造这么一棵树,在插入和删除的时候也要根据一定的规则进行操作,这些操作在一定情况下也会影响到整棵树的使用效率,所以,我们想有没有这么一种树,我们并不必严格要求这棵树要平衡度很高(比如所有路径的长度差都必须在一个很小的范围之内)以提高插入和删除的效率,同时又不能太影响到查找的效率,已达到一个比较好的使用效果。
在此之前,本文图例约定如下:
红黑树(Red Black Tree - RB Tree)就是这样一种数据结构,和很多数据结构一样,红黑树也有自己的一套事先规定好的规则,无论在什么状态下,一颗红黑树都必须满足以下五个规则(定义), 破坏任何一条规则都不再是一颗红黑树。
1. 红黑树的节点不是红色的就是黑色的
2. 红黑树的根节点永远是黑色的
3. 所有叶子节点都是黑色的(注意:红黑树的叶子节点是指Nil节点)
4. 同一路径上不能有相邻两个节点都是红色的
5. 从任一节点到所有叶子节点所经历的黑色节点个数相同
以上五个定义即使不能背下来,也要十分熟悉。用以上的定义去实现一颗红黑树,能使所有搜索路径长度相差最大不过一倍。
定义红黑树节点的数据结构:
public class TreeNode { private int elem; private TreeNode left, right; private TreeNode parent; private NodeColor color; public TreeNode (int elem) { this.elem = elem; color = NodeColor.RED; } }
比普通二叉搜索树多了一个属性表示节点颜色,初始化一个节点的时候,节点颜色设置为红色,因为插入一个红色节点,只要不违反红黑树的规则,插入之后不需要对树进行调整,但如果直接插入一个黑色节点,那肯定会违反上面所说的第5个规则,势必要进行调整,所以多一事不如少一事。
在此之前先讲一些基本操作,然后再讲具体
红黑树的基本操作包括染色和旋转,染色没有什么可说的,根据上面所说的第一条定义,染色无非是把一个节点从黑色染成红色或反之。
旋转包括右旋和左旋,具体的操作图例和代码从我之前写的一篇文章复制过来就好。
右旋:
做法是以A节点为轴,节点A的左子树指向其左孩子B的右子树2,然后节点B的左子树指向节点A,然后原本节点A的父节点R对应的子树指向节点B,其他节点不作变化,这边便完成了左旋操作。
相应的代码如下:以A点为轴进行右旋
private void rotateRight(TreeNode pivot) { TreeNode leftChild = pivot.getLeft(); TreeNode grandChildRight = leftChild.getRight(); TreeNode parent = pivot.getParent(); if (null == parent) { this.root = leftChild; } else if (pivot == parent.getLeft()) { parent.setLeft(leftChild); } else { parent.setRight(leftChild); } leftChild.setParent(parent); pivot.setLeft(grandChildRight); if (null != grandChildRight) { grandChildRight.setParent(pivot); } leftChild.setRight(pivot); pivot.setParent(leftChild); }