本文主要解析java8中HashMap的紅黑樹源碼部分,如有不懂可以先看我寫的紅黑樹基礎-第一篇
1.左旋
注意:圖中節點標號跟下面代碼中變量名一致,便於理解。
/**
* 紅黑樹節點左旋操作
* @param root 根節點
* @param p 旋轉節點
* @return 新root節點
*/
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
//1.把r的左子樹變成p的右子樹,並標記爲rl,即p->right=rl
if ((rl = p.right = r.left) != null)
//2.如果rl不爲空,鏈起來,rl->parent=p
rl.parent = p;
//3.把p的父節點變成r的父節點,並標記爲pp,即r->parent=pp
if ((pp = r.parent = p.parent) == null)
//4.如果pp爲空,那麼之前p是根節點,現在左旋後r變成根節點,root=r,並且根節點爲黑色,r.red=false
(root = r).red = false;
//5.否則pp不爲空,就判斷p是否是pp的左孩子,如果是,旋轉後r就是左孩子
else if (pp.left == p)
pp.left = r;
else
//6.否則就是右孩子
pp.right = r;
//7.最後完善樹的父子關係,鏈起來
//r->left=p
r.left = p;
//p->parent=r
p.parent = r;
}
//最後返回新的root節點
return root;
}
2.右旋
/**
* 紅黑樹節點右旋操作
* @param root 根節點
* @param p 旋轉節點
* @return 新root節點
*/
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
TreeNode<K,V> p) {
TreeNode<K,V> l, pp, lr;
if (p != null && (l = p.left) != null) {
//1.把l的右子樹變成p的左子樹,並標記爲lr,即p->left=lr
if ((lr = p.left = l.right) != null)
//2.如果lr不爲空,鏈起來,lr->parent=p
lr.parent = p;
//3.把p的父節點變成l的父節點,並標記爲pp,即l->parent=pp
if ((pp = l.parent = p.parent) == null)
//4.如果pp爲空,那麼之前p是根節點,現在右旋後l變成根節點,root=l,並且根節點爲黑色,l.red=false
(root = l).red = false;
//5.否則pp不爲空,就判斷p是否是pp的右孩子,如果是,旋轉後l就是右孩子
else if (pp.right == p)
pp.right = l;
else
//6.否則就是左孩子
pp.left = l;
//7.最後完善樹的父子關係,鏈起來
//l->right=p
l.right = p;
//p->parent=l
p.parent = l;
}
//最後返回新的root節點
return root;
}
3.平衡插入
講解平衡插入之前,我們直接從源碼入手,如果感覺理解起來有困難,請先看懂我寫的紅黑樹基礎-第一篇
下面約定下面代碼變量的命名規則,x爲邏輯上待插入的節點(有可能是第一次插入,也有可能是自底向上調整時的插入),xp爲x節點的父節點,xpp爲x節點的祖父節點,也是xp的父節點。xppl是xpp的左孩子,xppr是xpp的右孩子。xp是xppl和xppr中的其中之一。
/**
* 紅黑樹節點平衡插入操作
* @param root 根節點
* @param x 插入節點
* @return 新root節點
*/
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//首先插入節點x的顏色初始化爲紅色
x.red = true;
//for循環沒有結束條件,自底向上插入循環處理,直至到root或者紅黑自平衡退出循環
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//如果xp爲空,說明x已經爲root了
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
//否則判斷父節點xp是否是黑色,或者xp是否爲root
else if (!xp.red || (xpp = xp.parent) == null)
return root;
//判斷父節點xp是否是祖父節點的左孩子
if (xp == (xppl = xpp.left)) {
//叔叔節點存在,並且爲紅節點
if ((xppr = xpp.right) != null && xppr.red) {
//紅黑樹基礎-第一篇,根據情景4.1進行變色
xppr.red = false;
xp.red = false;
xpp.red = true;
//以xpp爲插入節點,進行下一次循環
x = xpp;
}
//否則叔叔節點要麼爲空,要麼爲黑色
else {
//如果插入節點x是父節點xp的右孩子,那麼滿足紅黑樹基礎-第一篇,情景4.2.2
if (x == xp.right) {
//把xp進行左旋,同時把插入節點x賦值爲xp
root = rotateLeft(root, x = xp);
//重新設置xp和xpp
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//進行紅黑樹基礎-第一篇,情景4.2.1的處理
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
//xpp右旋
root = rotateRight(root, xpp);
}
}
}
}
//否則父節點xp是祖父節點的右孩子
else {
//叔叔節點存在,並且爲紅節點
if (xppl != null && xppl.red) {
//紅黑樹基礎-第一篇,根據情景4.1進行變色
xppl.red = false;
xp.red = false;
xpp.red = true;
//以xpp爲插入節點,進行下一次循環
x = xpp;
}
//否則叔叔節點要麼爲空,要麼爲黑色
else {
//如果插入節點x是父節點xp的左孩子,那麼滿足紅黑樹基礎-第一篇,情景4.3.2
if (x == xp.left) {
//把xp進行右旋,同時把插入節點x賦值爲xp
root = rotateRight(root, x = xp);
//重新設置xp和xpp
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//進行紅黑樹基礎-第一篇,情景4.3.1的處理
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
//xpp左旋
root = rotateLeft(root, xpp);
}
}
}
}
}
}
4.平衡刪除
未完待續