这个树,怎么一下就平衡了?(二)
四种不平衡情况处理
针对四种不平衡的情况,这里对每种情况进行详细的讲解。
RR平衡旋转(左单旋转)
这里的RR指的是节点模型的样子,其含义是需要左单旋转(记忆时候需要注意一下RR不是右旋转)!
出现这种情况的原因是节点的右侧的右侧较深这时候不平衡节点需要左旋,再细看过程。
- 在左旋的过程中,root(oldroot)节点下沉,中间节点(newroot)上浮.而其中中间节点(newroot)的右侧依然不变。
- 它上浮左侧所以需要指向根节点(oldroot)(毕竟一棵树)。但是这样newroot原来左侧节点H空缺。而我们需要仍然让整个树完整并且满足二叉排序树的规则。
- 而刚好本来oldroot右侧指向newroot现在结构改变oldroot右侧空缺,刚好这个位置满足在oldroot的右侧,在newroot的左侧,所以我们将H插入在这个位置。
- 其中H可能为NULL,不过不影响操作!
其更详细流程为:
而左旋的代码可以表示为:
private node getRRbanlance(node oldroot) {//右右深,需要左旋
// TODO Auto-generated method stub
node newroot=oldroot.right;
oldroot.right=newroot.left;
newroot.left=oldroot;
oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原来的root的高度需要从新计算
return newroot;
}
LL平衡旋转(右单旋转)
而右旋和左旋相反,但是思路相同,根据上述进行替换即可!
代码:
private node getLLbanlance(node oldroot) {//LL小,需要右旋转
// TODO Auto-generated method stub
node newroot=oldroot.left;
oldroot.left=newroot.right;
newroot.right=oldroot;
oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原来的root的高度需要从新金酸
return newroot;
}
RL平衡旋转(先右后左双旋转)
这个RL你可能有点懵圈,为啥RR叫左旋,LL叫右旋,这个RL怎么就叫先右后左旋转了?
别急别急,这个之所以先后后左,是因为具体需要中间节点右旋一次,然后上面节点左旋一次才能平衡,具体可以下面慢慢看。
首先产生这种不平衡的条件原因是:ROOT节点右侧左侧节点的深度高些,使得与左侧的差大于1,这个与我们前面看到的左旋右旋不同因为旋转一次无法达到平衡!
对于右左结构,中间(R)的最大,两侧(ROOT,R.L)的最小,但是下面(R.L)的比上面(ROOT)大(R.L在ROOT右侧)所以如果平衡的话,那么R.L应该在中间,而R应该在右侧,原来的ROOT在左侧。
这个过程节点的变化浮动比较大,需要妥善处理各个子节点的移动使其满足二叉排序树的性质!
这种双旋转具体实现其实也不难,不要被外表唬住,这里面双旋转我提供两种解答方法。
思路(标准答案)1:两次旋转RR,LL
这个处理起来非常容易,因为前面已经解决RR(左旋),LL(右旋)的问题,所以这里面在上面基础上可以直接解决,首先对R节点进行一次LL右旋,旋转一次之后R在最右侧,这就转化成RR不平衡旋转的问题了,所以这个时候以ROOT开始一次RR左旋即可完成平衡,具体流程可以参考下面这张图。
思路(个人方法)2:直接分析
根据初始和结果的状态,然后分析各个节点变化顺序=,手动操作这些节点即可。其实不管你怎么操作,只要能满足最后结构一致就行啦!
首先根据ROOT,R,R.L三个节点变化,R.L肯定要在最顶层,左右分别指向ROOT和R,那么这其中R.left,ROOT.right发生变化(原来分别是R.L和R)暂时为空。而刚好根据左右大小关系可以补上R.L原来的孩子节点A,B。
代码为:(注释部分为方案1)
private node getRLbanlance(node oldroot) {//右左深
// node newroot=oldroot.right.left;
// oldroot.right.left=newroot.right;
// newroot.right=oldroot.right;
// oldroot.right=newroot.left;
// newroot.left=oldroot;
// oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
// newroot.right.height=Math.max(getHeight(newroot.right.left),getHeight(newroot.right.right))+1;
// newroot.height=Math.max(getHeight(oldroot.left),getHeight(newroot.right))+1;//原来的root的高度需要从新金酸
oldroot.right =getLLbanlance(oldroot.right);
oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
return getRRbanlance(oldroot);
}
LR平衡旋转(先左后右双旋转)
这个情况和RL情况相似,采取相同操作即可。
根据上述RL修改即可
这部分代码为
private node getLRbanlance(node oldroot) {
oldroot.left =getRRbanlance(oldroot.left);
oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
return getLLbanlance(oldroot);
}
文章转自公众号:bigsai