1. 类的定义
static final class TreeNode extends LinkedHashMap.Entry
从定义中可以看出
-
TreeNode 是一个HashMap的一个静态内部类,且使用final修改,不能被继承
-
TreeNode继承LinkedHashMap.Entry,表明TreeNode是一个键值对的对象
2. 字段属性
//父节点
TreeNode parent; // red-black tree links
//左子节点
TreeNode left;
//右子节点
TreeNode right;
//前一个节点
TreeNode prev; // needed to unlink next upon deletion
//红黑标记
boolean red;
//hash值,父类继承
final int hash;
//key 父类继承
final K key;
//value 父类继承
V value;
//下一个节点 父类继承
Node next;
从字段属性中可以看出
- TreeNode既是红黑树结构也是双向链表结构
3. 构造函数
TreeNode(int hash, K key, V val, Node next) {
//调用父类的构造函数
super(hash, key, val, next);
}
4. 方法
root 方法
//获取根节点
final TreeNode root() {
//从当前节点循环,一直向上找父节点,直到节点的父节点为null,则此节点就是根节点
//无限循环,把当前节点赋值给r
for (TreeNode r = this, p;;) {
//查找r的父节点
if ((p = r.parent) == null)
//如果父节点为空,此节点为根节点
return r;
//把父节点赋值给r,继续循环
r = p;
}
}
moveRootToFront 方法
//把root元素添加到tab对应插槽的第一个元素,此方法针对链表结构进行操作,不对红黑树操作
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
//记录tab的长度
int n;
//如果root不为空,tab不为空,tab长度大于0
if (root != null && tab != null && (n = tab.length) > 0) {
//通过(n - 1) & root.hash计算root在tab插槽的位置
int index = (n - 1) & root.hash;
//获取root的hash值tab中对应插槽的第一个元素
TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
//如果root不是对一个元素
if (root != first) {
//存储root的后一个元素
Node<K,V> rn;
//把root设置为对应插槽的第一个元素
tab[index] = root;
//获取root前一个元素
TreeNode<K,V> rp = root.prev;
//如果root的下一个元素不为空
if ((rn = root.next) != null)
//把root的后一个元素的前一个元素引用指向root的前一个元素,即跳过root元素
((TreeNode<K,V>)rn).prev = rp;
//如果root的前一个元素不为空
if (rp != null)
//把root的上一个元素的后一个元素引用指向root的后一个元素,即跳过root元素
rp.next = rn;
//如果插槽中以前的第一个元素不为空
if (first != null)
//把以前的第一个元素的前一个元素的引用指向root
first.prev = root;
//root的后一个元素的引用指向以前的第一个元素
root.next = first;
//root的前一个元素的引用置为null
root.prev = null;
}
assert checkInvariants(root);
}
}
find 方法
//根据hash值和key查找元素
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
//获取当前元素,后面表示当前遍历的元素
TreeNode<K,V> p = this;
do {
//ph 当前元素的hash值
//dir 小于等于0,往左子节点查找,大于0往右子节点查找
//pk 当前元素的key
int ph, dir; K pk;
//pl 当前元素的左节点
//pr 当前元素的右节点
TreeNode<K,V> pl = p.left, pr = p.right, q;
if ((ph = p.hash) > h)
//如果当前元素的hash值大于传入的hash值,当前元素设置为当前元素的左节点
p = pl;
else if (ph < h)
//如果当前元素的hash值小于传入的hash值,当前元素设置为当前元素的右节点
p = pr;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
//如果当前元素的key等于传入的key,表示找到,直接返回当前元素即可
return p;
else if (pl == null)
//如果当前元素的左节点为空,当前元素设置为当前元素的右节点
p = pr;
else if (pr == null)
//如果当前元素的右节点为空,当前元素设置为当前元素的左节点
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
//如果类不相等或者k小于pk当前元素设置为当前元素的左节点,反之设置为右节点
p = (dir < 0) ? pl : pr;
else if ((q = pr.find(h, k, kc)) != null)
//递归查找,找到就返回
return q;
else
p = pl;
//继续循环
} while (p != null);
//没找到,返回null
return null;
}
getTreeNode 方法
final TreeNode<K,V> getTreeNode(int h, Object k) {
//通过调用find方法查找
return ((parent != null) ? root() : this).find(h, k, null);
}
removeTreeNode 方法
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
boolean movable) {
//注意这是当前节点的方法,有个this引用存在,删除的是当前节点
int n;
//如果table为空直接返回
if (tab == null || (n = tab.length) == 0)
return;
//根据hash值获取对应插槽的位置
int index = (n - 1) & hash;
//获取插槽第一个节点
//first 插槽中第一个节点
//root 红黑树的根节点
//rl 根节点的左子节点
TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
//succ 下一个节点的副本
//pred 上一个节点的副本
TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
//这里能体现出红黑树结构内部也有链表结构
if (pred == null)
//如果上一个节点为null,说明当前节点是第一个节点
//因为是删除的是当前节点,所以插槽第一个节点赋值为下一个节点
tab[index] = first = succ;
else
//如果上一个节点不为null,把上一个节点的引用指向下一个节点
pred.next = succ;
if (succ != null)
//如果下一个节点不为null,把下一个节点的上一个节点的引用指向当前节点的上一个节点
succ.prev = pred;
if (first == null)
//如果第一个节点为null,表示插槽没有元素,直接返回
//这里表示最开始插槽中只有当前元素一个元素,实际上不会有这种可能
return;
if (root.parent != null)
//如果根节点还存在父节点,把根节点置修改为树的根节点
root = root.root();
if (root == null || root.right == null ||
(rl = root.left) == null || rl.left == null) {
//如果根节点为null 根节点的右子节点为null 根节点的左子节点为null 根节点的左子节点的左子节点为null
//这些都表示当前插槽的节点数量太少,将红黑树转换为链表 直接返回
tab[index] = first.untreeify(map); // too small
return;
}
//p 当前节点的副本
//pl 当前节点的左子节点
//pr 当前节点的右子节点
//replacement 需要替换的节点
TreeNode<K,V> p = this, pl = left, pr = right, replacement;
//------------------------------删除一个节点只有四种情况--------------------------------
//******************第一种情况 删除的节点集邮左子树又有右子树***********
//******************把当前节点和当前节点的右子节点的最左子节点对换***********
if (pl != null && pr != null) {
//s 当前节点的右子节点的最左边的子节点
TreeNode<K,V> s = pr, sl;
//一直向左遍历得到最左的子节点
while ((sl = s.left) != null) // find successor
s = sl;
//把当前节点的颜色和最左的子节点颜色互换
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
//sr s的右子节点
TreeNode<K,V> sr = s.right;
//pp 当前节点p的父节点
TreeNode<K,V> pp = p.parent;
if (s == pr) { // p was s's direct parent
//如果s等于当前节点的右子节点,这句表示pr没有左子节点
//把p作为s的右子节点
p.parent = s;
s.right = p;
}
else {
//其他情况,pr存在左子节点
//sp s的父节点
TreeNode<K,V> sp = s.parent;
//p的父节点指向sp
if ((p.parent = sp) != null) {
//如果sp不为null,表示pr不为null,即p存在右子节点
if (s == sp.left)
//如果s是父节点的左子节点
//把sp的左子节点设置为当前节点
sp.left = p;
else
//如果s是父节点的右子节点
//把sp的右子节点设置为当前节点
sp.right = p;
}
//把pr作为s的右子节点
if ((s.right = pr) != null)
//如果pr不为null,把pr的父节点指向s
pr.parent = s;
}
//把当前节点的左节点置为null
p.left = null;
//把当前节点的右子节点指向s的右子节点
if ((p.right = sr) != null)
//如果当前节点的右子节点不为null
//把s的右子节点的父节点指向当前节点
sr.parent = p;
//把当前节点的左子节点设置为s的左子节点
if ((s.left = pl) != null)
//如果当前节点的左子节点不为null
//把当前节点的左子节点的父节点指向s节点
pl.parent = s;
//当把s节点的父节点设置为前节点的父节点
if ((s.parent = pp) == null)
//如果当前节点的父节点为null
//s节点为根节点
root = s;
else if (p == pp.left)
//如果当当前节点为父节点的左子节点
//把当前节点的父节点的左子节点设置为s节点
pp.left = s;
else
//把当前节点的父节点的右子节点设置为s节点
pp.right = s;
if (sr != null)
//如果s节点的右子节点不为null
//需要替换s节点的右子节点
replacement = sr;
else
//如果s节点的右子节点为null
//需要替换的节点为当前节点
replacement = p;
}
//******************第二种情况 删除的节点只有左子树***********
else if (pl != null)
//如果当前节点的右子节点为空,删除的节点只有左子树
//替换当前节点的左子节点
replacement = pl;
//******************第三种情况 删除的节点只有右子树***********
else if (pr != null)
//如果当前节点的左子节点为空
//替换当前节点的右子节点
replacement = pr;
//******************第三种情况 删除的节点是叶子节点***********
else
//如果当前节点的左右子节点都为null
//替换当前节点
replacement = p;
//------------------------------end--------------------------------
//****************替换的节点不是当前节点,表示只有左子节点或者右子节点***************
//****************直接使用replacement替换p***************
if (replacement != p) {
//如果要替换的节点不是当前节点
//pp 当前节点的父节点
//被替换节点的父节点指向当前节点父节点
TreeNode<K,V> pp = replacement.parent = p.parent;
if (pp == null)
//当前节点的父节点为null
//根节点指向要被替换的节点
root = replacement;
else if (p == pp.left)
//如果当前节点是当前节点父节点的左子节点
//当前节点的父节点的左子节点指向要被替换的节点
pp.left = replacement;
else
//如果当前节点是当前节点父节点的右子节点
//当前节点的父节点的右子节点指向要被替换的节点
pp.right = replacement;
//当前节点的左子节点 右子节点 父节点都置为null
//移除当前节点
p.left = p.right = p.parent = null;
}
//****************end***************
//如果当前节点是红节点 r指向根节点
//如果当前节点是黑节点 r指向删除后重新平衡的根节点
TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
//*********************移除当前节点*****************
if (replacement == p) { // detach
//如果被替换的节点是当前节点
//pp 当前节点的父节点副本
TreeNode<K,V> pp = p.parent;
//当前解得父节点引用设置为null
p.parent = null;
if (pp != null) {
//如果当前节点的父节点不为null
if (p == pp.left)
//如果当前节点是当前节点父节点的左子节点
//把当前节点父节点的左子节点引用置为null
pp.left = null;
else if (p == pp.right)
//如果当前节点是当前节点父节点的右子节点
//把当前节点父节点的右子节点引用置为null
pp.right = null;
}
}
//*********************end*****************
if (movable)
//如果movable为true
//把删除后重新平衡的根节点放到插槽的第一个位置
moveRootToFront(tab, r);
}
treeify 方法
//从链表转换为红黑树
final void treeify(Node<K,V>[] tab) {
//保存根节点,也是tab对应插槽的第一个节点
TreeNode<K,V> root = null;
//for循环遍历整个链表,只要还有下一个节点就继续遍历
//next保存当前遍历节点的下一个节点
for (TreeNode<K,V> x = this, next; x != null; x = next) {
//获取当前节点的下一个节点
next = (TreeNode<K,V>)x.next;
//把当前节点的左右子节点都置为null
x.left = x.right = null;
if (root == null) {
//如果根节点为null
//把当前节点置为根节点,并将颜色设置为黑色,红黑树根节点必须为黑色
//把当前节点的父节点引用置为null,根节点没有父节点
x.parent = null;
x.red = false;
root = x;
}
else {
//获取当前节点的key
K k = x.key;
//获取当前节点的hash值
int h = x.hash;
Class<?> kc = null;
//内层循环的意义在于插入外层循环的节点到红黑树中
//初始化时,把p指向根节点,进行循环操作,循环中p表示内层循环的当前节点
for (TreeNode<K,V> p = root;;) {
//dir 小于0指向左子节点,大于0指向右子节点
//ph 存储p的hash值
int dir, ph;
//获取p的key
K pk = p.key;
if ((ph = p.hash) > h)
//如果p的hash值大于当前节点的hash值,往左
dir = -1;
else if (ph < h)
//如果p的hash值小于当前节点的hash值,往右
dir = 1;
//比较p节点的key和当前节点的key
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
//xp 存储p节点的副本
TreeNode<K,V> xp = p;
//dir大于0 p指向p的右子节点; dir小于等于0 p指向p的左子节点
//找到对应的位置,并且p节点对应的子节点为空,进行插入
if ((p = (dir <= 0) ? p.left : p.right) == null) {
//把p节点的副本设置为当前节点的父节点
x.parent = xp;
if (dir <= 0)
//如果dir小于等于0 当前节点作为p节点的左子节点
xp.left = x;
else
//如果dir大于0 当前节点作为p节点的右子节点
xp.right = x;
//插入后进行红黑树平衡操作,并重新调整根节点
root = balanceInsertion(root, x);
//退出循环
break;
}
//如果当前节点对应p节点的左子节点或者右子节点不为空,把p设置为对应的子节点继续循环遍历
}
}
}
//把最后得到的根节点插入对应插槽的第一个位置
moveRootToFront(tab, root);
}
balanceInsertion 方法
//插入x节点后进行平衡操作
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//默认插入的是红节点
x.red = true;
//for循环遍历
//xp 存储x节点的父节点
// xpp 存储x节点的祖父节点,父节点的父节点
//xppl 存储x节点的左叔叔节点,父节点的父节点的左子节点
//xppr 存储x节点的右叔叔节点,父节点的父节点的右子节点
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
if ((xp = x.parent) == null) {
// 如果父节点为空,表示当前节点为根节点
//把当前节点设置为黑节点,并返回
x.red = false;
return x;
}
else if (!xp.red || (xpp = xp.parent) == null)
//如果父节点为黑节点 或者 祖父节点为null
//直接返回root节点,不需要修改
return root;
//以下情况需要旋转个染色达到新的平衡
  //1.插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色。操作:将当前节点的父节点和叔叔节点涂黑,将祖父节点涂红,再将当前节点指向其祖父节点,再次从新的当前节点开始算法
   //2.插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的右子节点。操作:将当前节点的父节点作为新的节点,以新的当前节点为支点做左旋操作
   //3.插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的左子节点。操作:将当前节点的父节点涂黑,将祖父节点涂红,在祖父节点为支点做右旋操作。最后把根节点涂黑,整个红-黑树重新恢复了平衡
if (xp == (xppl = xpp.left)) {
//如果父节点是祖父节点的左子节点
if ((xppr = xpp.right) != null && xppr.red) {
//如果右叔叔节点不为null并且右叔叔节点为红节点
//右叔叔节点设置为黑节点
xppr.red = false;
//父节点设置为黑节点
xp.red = false;
//祖父节点设置为红节点
xpp.red = true;
//把当前节点指向祖父节点,然后进行下一次循环
x = xpp;
}
else {
//其他情况,右叔叔节点为null或者右叔叔节点是黑节点
if (x == xp.right) {
//如果当前节点是父节点的右子节点
//当前节点指向父节点并且作为基础节点进行左旋
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 {
//其他情况,如果父节点是祖父节点的右子节点
if (xppl != null && xppl.red) {
//如果左叔叔节点不为null并且左叔叔节点是红节点
//左叔叔节点设置为黑节点
xppl.red = false;
//父节点设置为黑节点
xp.red = false;
//祖父节点设置为红节点
xpp.red = true;
//当前节点指向祖父节点,然后进行下一次循环
x = xpp;
}
else {
//其他情况,如果左叔叔节点不为null或者左叔叔节点是黑节点
if (x == xp.left) {
//如果当前节点是父节点的左子节点
//当前节点指向父节点并作为基础节点进行右旋
root = rotateRight(root, x = xp);
//重新为祖父节点赋值
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
//如果父节点不为null
//父节点设置为黑节点
xp.red = false;
if (xpp != null) {
//如果祖父节点不为null
//祖父节点设置为红节点
xpp.red = true;
//祖父节点作为基础节点进行左旋
root = rotateLeft(root, xpp);
}
}
}
}
}
}
分析:
-
情形一:如果当前节点的父节点是祖父节点的左子节,右叔叔节点为红节点
- 进行染色,把父节点和右叔叔节点染色为黑节点,祖父节点染色为红节点
- 把祖父节点设置为当前节点继续进行平衡操作
-
情形二:如果当前节点的父节点是祖父节点的左子节,右叔叔节点为黑节点
-
如果当前节点是父节点的右子节点
-
把当前节点指向父节点
-
以当前节点的父节点为基础节点进行左旋
-
重新计算父节点和祖父节点
-
-
如果父节点不为空
-
把父节点染色为黑节点
-
如果祖父节点不为空
- 把祖父节点染色为红节点
- 以祖父节点为基础节点进行右旋
-
-
-
情形三:如果父节点是祖父节点的右子节点,左叔叔点为红节点
- 进行染色,把左叔叔节点染色为黑节点,父节点染色为黑节点,祖父节点染色为红节点
- 把祖父节点置为当前节点继续进行平衡操作
-
情形四:如果父节点是祖父节点的右子节点,左叔叔节点为黑节点
- 如果当前节点是父节点的左子节点
- 把当前节点指向父节点
- 以当前节点的父节点为基础节点进行右旋
- 重新计算当前节点的父节点和祖父节点
- 如果父节点不为空
- 把父节点染色为黑节点
- 如果祖父节点不为空
- 把祖父节点染色为红节点
- 以祖父节点为基础节点进行左旋
- 如果当前节点是父节点的左子节点
rotateLeft 方法
//左旋
/**
* 左旋示意图:对节点x进行左旋
* p p
* / /
* x y
* / \ / \
* lx y -----> x ry
* / \ / \
* ly ry lx ly
*/
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
//r 存储当前节点的右子节点
//rl 存储当前的右子节点的左子节点
//pp 存储当前节点父节点
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
//如果当前节点不为null 并且 当前节点的右子节点不为null
//当前节点的右子节点指向当前节点的右子节点的左子节点
if ((rl = p.right = r.left) != null)
//rl的父节点指向当前节点
rl.parent = p;
//当前节点的右子节点的父节点指向当前节点的父节点
if ((pp = r.parent = p.parent) == null)
//如果r的父节点为空,把r设置为根节点,并设置为黑节点
(root = r).red = false;
else if (pp.left == p)
//如果当前节点是父节点的左子节点
//当前节点的父节点的左子节点指向当前节点的右子节点
pp.left = r;
else
//如果当前节点是父节点的右子节点
//当前节点的父节点的右子节点指向当前节点的右子节点
pp.right = r;
//当前节点的右子节点的左子节点指向当前节点
r.left = p;
//当前节点的父节点指向当前节点的右子节点
p.parent = r;
}
//返回根节点
return root;
}
左旋:
- 当前节点p的右子节点pr作为当前节点的父节点
- 当前节点p作为当前节点右子节点pr的左子节点
- 当前节点的右子节点pr的左子节点prl作为当前节点p的右子节点
rotateRight 方法
//右旋
/**
* 右旋旋示意图:对节点y进行右旋
* p p
* / /
* y x
* / \ / \
* x ry -----> lx y
* / \ / \
* lx rx rx ry
*/
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
TreeNode<K,V> p) {
//l 当前节点的左子节点
//当前节点的左子节点的右子节点
TreeNode<K,V> l, pp, lr;
if (p != null && (l = p.left) != null) {
//如果当前节点p不为null 并且 当前节点p的左子节点l不为null
//当前节点p的左子节点l指向当前节点p的左子节点l的右子节点lr
if ((lr = p.left = l.right) != null)
//如果当节点的左子节点的右子节点不为null
//当前节点的左子节点的右子节点指向当前节点
lr.parent = p;
//当前节点的左子节点的父节点指向当前节点的父节点
if ((pp = l.parent = p.parent) == null)
//如果当前节点的父节点为null
//根节点指向当前节点左子节点
//把当前节点的左子节点置为黑节点
(root = l).red = false;
else if (pp.right == p)
//如果当前节点是父节点的右子节点
//当前节点的父节点的右子节点指向当前节点的左子节点
pp.right = l;
else
//其他情况,当前节点的父节点的左子节点指向当前节点的坐子节点
pp.left = l;
//当前节点的左子节点的右节点指向当前节点
l.right = p;
//当前节点的父节点指向当前节点的左子节点
p.parent = l;
}
//返回根节点
return root;
}
右旋:
- 当前节点p的左子节点pl作为当前节点p的父节点
- 当前节点p作为当前节点的左子节点pl的右子节点
- 当前节点p的的左子节点pl的右子节点plr作为当前节点的左子节点
balanceDeletion 方法
//删除平衡
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//最外层是一个无限for循环,不断调整x的位置,直到整棵树达到平衡了才中断循环
//xp x的父节点
//xpl x父节点的左子节点 左兄弟节点
//xpr x父节点的右子节点 右兄弟节点
for (TreeNode<K,V> xp, xpl, xpr;;) {
if (x == null || x == root)
//如果x是null或者是根节点直接返回根节点
return root;
else if ((xp = x.parent) == null) {
//如果x的父节点为null,表示当前节点是根节点
//把当前节点置为黑节点,并返回
x.red = false;
return x;
}
else if (x.red) {
//如果x节点是红节点
//把x节点染色为黑节点,并返回根节点
x.red = false;
return root;
}
else if ((xpl = xp.left) == x) {
//如果x是父节点的左子节点
if ((xpr = xp.right) != null && xpr.red) {
//x的右兄弟节点不为null 并且 右兄弟节点为红节点
//把右兄弟节点染色为黑节点
xpr.red = false;
//父节点染色为红节点
xp.red = true;
//以父节点为基础节点进行左旋
root = rotateLeft(root, xp);
//重新为xp和xpr赋值
xpr = (xp = x.parent) == null ? null : xp.right;
}
if (xpr == null)
//如果x的右兄弟节点不存在
//x指向父节点
x = xp;
else {
//右兄弟节点存在的情况
//sl 右兄弟节点的左子节点
//sr 右兄弟节点的右子节点
TreeNode<K,V> sl = xpr.left, sr = xpr.right;
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
//如果右兄弟的子节点为空或者为黑节点
//把右兄弟节点染色为红节点
xpr.red = true;
//x指向父节点
x = xp;
}
else {
//右兄弟节点不全为null并且为红节点的情况
if (sr == null || !sr.red) {
//如果右兄弟节点的右子节点为null或者为黑节点
if (sl != null)
//如果右兄弟节点的左子节点不为null
//把右兄弟节点的左子节点染色为黑节点
sl.red = false;
//把右兄弟节点染色为红节点
xpr.red = true;
//以右兄弟节点为基础节点进行右旋
root = rotateRight(root, xpr);
//重新设置x的父节点和右兄弟节点
xpr = (xp = x.parent) == null ?
null : xp.right;
}
if (xpr != null) {
//如果右兄弟节点不为null
//如果父节点为null 右兄弟节点染色为黑节点
//如果父节点不为null 右兄弟节点染色为父节点的颜色
xpr.red = (xp == null) ? false : xp.red;
if ((sr = xpr.right) != null)
//如果右兄弟节点的右子节点不为null
//右兄弟节点的右子节点染色为黑节点
sr.red = false;
}
if (xp != null) {
//如果父节点不为null
//父节点染色为黑节点
xp.red = false;
//以父节点为基础节点进行左旋
root = rotateLeft(root, xp);
}
//把x节点指向根节点
x = root;
}
}
}
else { //对称性,如果x节点是父节点的右子节点
if (xpl != null && xpl.red) {
xpl.red = false;
xp.red = true;
root = rotateRight(root, xp);
xpl = (xp = x.parent) == null ? null : xp.left;
}
if (xpl == null)
x = xp;
else {
TreeNode<K,V> sl = xpl.left, sr = xpl.right;
if ((sl == null || !sl.red) &&
(sr == null || !sr.red)) {
xpl.red = true;
x = xp;
}
else {
if (sl == null || !sl.red) {
if (sr != null)
sr.red = false;
xpl.red = true;
root = rotateLeft(root, xpl);
xpl = (xp = x.parent) == null ?
null : xp.left;
}
if (xpl != null) {
xpl.red = (xp == null) ? false : xp.red;
if ((sl = xpl.left) != null)
sl.red = false;
}
if (xp != null) {
xp.red = false;
root = rotateRight(root, xp);
}
x = root;
}
}
}
}
}
小结
红黑树现在了解的还不是很透彻,等后面数据结构专题再细说红黑树。数据结构最好还是结合图来说明更好
- 红黑树是二叉平衡树,通过变色,左旋,右旋达到平衡
- TreeNode是红黑树的结构,但也保留了链表的特性,这样适合快速转换为链表结构