首页 > 科技 >

HashMap红黑树

2019-08-19 03:35:32 暂无 阅读:843 评论:0

上篇文章说到,若是链表的长度>=7的时候,链表会转成红黑树,本文就来具体的剖析一下红黑树的扭转过程。

首先来看一段代码

HashMap红黑树

本文的提到的红黑树的转换首要是在hd.treeify(tab);这个方式里

具体代码如下,下面来剖析一下具体的过程final void treeify(Node<K,V>[] tab) {

TreeNode<K,V> root = null;

for (TreeNode<K,V> x = this, next; x != null; x = next) {

next = (TreeNode<K,V>)x.next;

x.left = x.right = null;

if (root == null) {

x.parent = null;

x.red = false;

root = x;

}

else {

K k = x.key;

int h = x.hash;

Class<?> kc = null;

for (TreeNode<K,V> p = root;;) {

int dir, ph;

K pk = p.key;

if ((ph = p.hash) > h)

dir = -1;

else if (ph < h)

dir = 1;

else if ((kc == null &&

(kc = comparableClassFor(k)) == null) ||

(dir = compareComparables(kc, k, pk)) == 0)

dir = tieBreakOrder(k, pk);

TreeNode<K,V> xp = p;

if ((p = (dir <= 0) ? p.left : p.right) == null) {

x.parent = xp;

if (dir <= 0)

xp.left = x;

else

xp.right = x;

root = balanceInsertion(root, x);

break;

}

}

}

}

moveRootToFront(tab, root);

}

具体看for轮回,第一次执行的时候,root一定是null,所以root指向第一个节点。

为了明确转换的过程,举一个例子如下图所示,假设我们有一个长度为5的链表,用这5个数来表述一下具体的转换的道理,这里举的例子对照简洁,其实只要这个会了,其他的也就一通百通。

HashMap红黑树

按照代码逻辑,第一次是root一定是会指向第一个节点,默认是该节点为黑,即如下图所示。

HashMap红黑树

第二次轮回,则会走到 else里,这里又有一个for轮回,这个for轮回的目的首要是找到合适的位置去插入第二个节点。

在判读hash的时候,我们发现链内外的第二个节点的hash为2,则会在右子树进行插入,究竟如下图所示:

HashMap红黑树

然后会执行到下面的方式

HashMap红黑树

具体的代码如下: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.red = false;

return x;

}

else if (!xp.red || (xpp = xp.parent) == null)

return root;

if (xp == (xppl = xpp.left)) {

if ((xppr = xpp.right) != null && xppr.red) {

xppr.red = false;

xp.red = false;

xpp.red = true;

x = xpp;

}

else {

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) {

xppl.red = false;

xp.red = false;

xpp.red = true;

x = xpp;

}

else {

if (x == xp.left) {

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);

}

}

}

}

}

}

在此次轮回里,我们能够获得一个结论,当前的节点会点窜为红树,结果如下:

HashMap红黑树

第三次轮回,按照以上的步伐,将在2的右节点插入3,结果如下图所示

HashMap红黑树

持续执行balanceInsertion这个方式,到这里就显现有意思的事情了,前方高能,注重旁观。

X节点指向3, root为跟节点指向1.

首先设置x节点为红,则整个树酿成了黑->红->红,显着不相符红黑树的特征,则意味着接下来要发生扭转来包管树的均衡。

下面来一行代码一行代码进行剖析。

首先界说了xp, xpp, xppl, xppr

四个暂时变量,从这里也能够看出每个变量代表的寄义。

xp:节点x的父亲节点

xpp:节点x的父亲节点的父亲节点xppl:节点x的父亲节点的父亲节点的左孩子节点

xppr:节点x的父亲节点的父亲节点的右孩子节点

这几个变量先搞清楚,有助于我们接下来的代码剖析。

HashMap红黑树

此时,几个变量指向的地址如图上的红色圆圈里所示。

按照上面的指示,代码会直接执行到下图所示代码块

HashMap红黑树

设置xp节点为黑,xpp节点为红。

紧接着执行root = rotateLeft(root, xpp);

这个方式,这里又有意思了,前方持续高能,即将发生左旋。

注重,这里传入的两个值为root和xpp,这一次两者指向统一个节点。

看下这个方式的具体代码

HashMap红黑树

r指向了p的右节点,此时一定不为空(参照带有红圆圈的那张图)即此时

r指向了节点2。

先来看第一个判断:if ((rl = p.right = r.left) != null)

此时r的左节点一定为空,则能够看出p的右节点此时不在指向r,而是指向了空节点,此时如下图所示:节点1跟右节点断开,节点1的右节点指向为空。

HashMap红黑树

此时r的left则为空,第一个if不知足。if ((pp = r.parent = p.parent) == null)

p此时指向的是根节点,所以parent一定为空,前提知足,

root节点移动,指向r节点,也就是如今的节点2,同时设置为黑

此时的究竟如下:

HashMap红黑树

最后一步:

p的parent节点指向r,r的left节点指向p,则本次左旋到此完成,最后的究竟如下所示:

HashMap红黑树

节点4和节点5的具体过程这里就不在胪陈了,扭转过程和这里很雷同,无非就是左旋酿成了右旋,有乐趣的小伙伴能够本身看下代码,整体过程跟我上面剖析的雷同。

到此,你看领略整个过程了吗?

进展对正在入坑java的小伙伴有所匡助,若是觉着对你有效,迎接收藏并转发,也能够私信我商议问题,感谢。

相关文章