刚刚被访问过的元素,极可能在不久之后再次被访问到
将被访问的下一元素,极有可能就处于不久之前被访问过的某个元素的附近
因此,只需将刚被访问的节点,及时地“转移”至树根(附近),即可加速后续的操作
随着节点E的逐层上升,两侧子树的结构也不断地调整,故这一过程也称作伸展, 而采用这一调整策略的二叉搜索树也因此得名
如此分摊下来,每次访问平均需要W(n)时间。很遗憾,这一效率不仅远远低于AVL树,而且甚至与原始的二叉搜索树的最坏情况相当。且经过以上连续的5次访问之后,全树的结构将会复原
将逐层伸展改为双层伸展。 具体地,每次都从当前节点v向上追溯两层(而不是仅一层),并根据其父亲p以及祖父g的相对 位置,进行相应的旋转。
每经过一次双层调整操作,节点v都会上升两层。若v的初始深度depth(v) 为偶数,则最终v将上升至树根。若depth(v)为奇数,则当v上升至深度为1时,不妨最后再相应 地做一次zig或zag单旋操作。无论如何,经过depth(v)次旋转后,v最终总能成为树根。
package com.atguigu.self;
/**
* @anthor shkstart
* @create 2020-08-07 19:54
*/
public class Splay<Integer> extends BST<Integer>{
//伸展算法
protected BinNode<Integer> splay(BinNode<Integer> v){
//v为因最近访问而需伸展的节点位置
if (v == null) return null;
BinNode<Integer> p = null;
BinNode<Integer> g = null;
while ((p == v.parent) && (g == p.parent)){
//自下而上,反复对v做双层伸展
BinNode<Integer> gg = g.parent;
//每轮之后v都以原曾祖父(great-grand parent)为父
if (IsLChild(v)){
if (IsLChild(p)){
//zig-zig
attachAsLChild(g,p.rc);
attachAsLChild(p,v.rc);
attachAsRChild(p,g);
attachAsRChild(v,p);
} else {
//zig-zag
attachAsLChild(p,v.rc);
attachAsRChild(g,v.lc);
attachAsLChild(v,g);
attachAsRChild(v,p);
}
} else if (IsRChild(p)){
//zag-zag
attachAsRChild(g,p.lc);
attachAsRChild(g,v.lc);
attachAsLChild(p,g);
attachAsLChild(v,p);
} else {
//zag-zig
attachAsRChild(p,v.lc);
attachAsLChild(g,v.lc);
attachAsRChild(v,g);
attachAsLChild(v,p);
}
if (gg == null) {
//若*v原先的曾祖父*gg不存在,则*v现在应为树根
v.parent = null;
} else {
//否则,*gg此后应该以*v作为左或右孩子
if (g == gg.lc){
attachAsLChild(gg,v);
} else {
attachAsRChild(gg,v);
}
}
updateHeight(g);
updateHeight(p);
updateHeight(v);
}
//双层伸展结束时,必有g == NULL,但p可能非空
if (p == v.parent){
//若p果真非空,则额外再做一次单旋
if (IsLChild(v)){
attachAsLChild(p,v.lc);
attachAsRChild(v,p);
} else {
attachAsRChild(p,v.lc);
attachAsLChild(v,p);
}
updateHeight(p);
updateHeight(v);
}
v.parent = null;
return v;
}
//调整之后新树根应为被伸展的节点,故返回该节点的位置以便上层函数更新树根
//查找算法
public BinNode<Integer> search(Integer e){
BinNode<Integer> p = searchIn(_root,e,_hot = null);
_root = splay((p != null) ? p : _hot );
return _root;
}
//插入算法
public BinNode<Integer> insert(Integer e){
if (_root == null){
_size++;
return _root = new BinNode<Integer>(e);
}
//处理原树为空的情况
if (e == search(e).data) return _root;
//确认目标节点不存在
_size++;
BinNode<Integer> t = _root;
//创建新节点。以下调整<=7个指针以完成局部重构
if ((int)_root.data < (int)e){
//插入新根,以t和t->rc为左、右孩子
t.parent = _root = new BinNode<Integer>(e,null,t,t.rc);
if (HasRChild(t)){
t.rc.parent = _root;
t.rc = null;
}
} else {
//插入新根,以t->lc和t为左、右孩子
t.parent = _root = new BinNode<Integer>(e,null,t.lc,t);
if (HasLChild(t)){
t.lc.parent = _root;
t.lc = null;
}
}
updateHeightAbove(t);
//更新t及其祖先(实际上只有_root一个)的高度
return _root;
//新节点必然置于树根,返回之
}
//删除算法
public Boolean remove(Integer e){
//从伸展树中删除关键码e
if ((_root == null) || (e != search(e).data)) return false;
//若树空或目标不存在,则无法删除
BinNode<Integer> w = _root;
//assert: 经search()后节点e已被伸展至树根
if (!HasLChild(_root)){
//若无左子树,则直接删除
_root = _root.rc;
if (_root != null) _root.parent = null;
} else if (!HasRChild(_root)){
//若无右子树,也直接删除
_root = _root.lc;
if (_root != null) _root.parent = null;
} else {
//若左右子树同时存在,则
BinNode<Integer> lTree = _root.lc;
lTree.parent = null;
_root.lc = null;
//暂时将左子树切除
_root = _root.rc;
_root.parent = null;
//只保留右子树
search(w.data);
//以原树根为目标,做一次(必定失败的)查找
_root.lc = lTree;
lTree.parent = _root;
// assert: 至此,右子树中最小节点必伸展至根,且(因无雷同节点)其左子树必空,于是只需将原左子树接回原位即可
}
if (_root != null){
updateHeight(_root);
//此后,若树非空,则树根的高度需要更新
}
return true;
//返回成功标志,若目标节点存在且被删除,返回true;否则返回false
}
public void attachAsLChild(BinNode<Integer> p,BinNode<Integer> lc){
p.lc = lc;
if (lc != null){
lc.parent = p;
}
}
public void attachAsRChild(BinNode<Integer> p,BinNode<Integer> lc){
p.rc = rc;
if (rc != null){
rc.parent = p;
}
}
}
//伸展算法
protected BinNode<Integer> splay(BinNode<Integer> v){
//v为因最近访问而需伸展的节点位置
if (v == null) return null;
BinNode<Integer> p = null;
BinNode<Integer> g = null;
while ((p == v.parent) && (g == p.parent)){
//自下而上,反复对v做双层伸展
BinNode<Integer> gg = g.parent;
//每轮之后v都以原曾祖父(great-grand parent)为父
if (IsLChild(v)){
if (IsLChild(p)){
//zig-zig
attachAsLChild(g,p.rc);
attachAsLChild(p,v.rc);
attachAsRChild(p,g);
attachAsRChild(v,p);
} else {
//zig-zag
attachAsLChild(p,v.rc);
attachAsRChild(g,v.lc);
attachAsLChild(v,g);
attachAsRChild(v,p);
}
} else if (IsRChild(p)){
//zag-zag
attachAsRChild(g,p.lc);
attachAsRChild(g,v.lc);
attachAsLChild(p,g);
attachAsLChild(v,p);
} else {
//zag-zig
attachAsRChild(p,v.lc);
attachAsLChild(g,v.lc);
attachAsRChild(v,g);
attachAsLChild(v,p);
}
if (gg == null) {
//若*v原先的曾祖父*gg不存在,则*v现在应为树根
v.parent = null;
} else {
//否则,*gg此后应该以*v作为左或右孩子
if (g == gg.lc){
attachAsLChild(gg,v);
} else {
attachAsRChild(gg,v);
}
}
updateHeight(g);
updateHeight(p);
updateHeight(v);
}
//双层伸展结束时,必有g == NULL,但p可能非空
if (p == v.parent){
//若p果真非空,则额外再做一次单旋
if (IsLChild(v)){
attachAsLChild(p,v.lc);
attachAsRChild(v,p);
} else {
attachAsRChild(p,v.lc);
attachAsLChild(v,p);
}
updateHeight(p);
updateHeight(v);
}
v.parent = null;
return v;
}
//调整之后新树根应为被伸展的节点,故返回该节点的位置以便上层函数更新树根
//查找算法
public BinNode<Integer> search(Integer e){
BinNode<Integer> p = searchIn(_root,e,_hot = null);
_root = splay((p != null) ? p : _hot );
return _root;
}
//插入算法
public BinNode<Integer> insert(Integer e){
if (_root == null){
_size++;
return _root = new BinNode<Integer>(e);
}
//处理原树为空的情况
if (e == search(e).data) return _root;
//确认目标节点不存在
_size++;
BinNode<Integer> t = _root;
//创建新节点。以下调整<=7个指针以完成局部重构
if ((int)_root.data < (int)e){
//插入新根,以t和t->rc为左、右孩子
t.parent = _root = new BinNode<Integer>(e,null,t,t.rc);
if (HasRChild(t)){
t.rc.parent = _root;
t.rc = null;
}
} else {
//插入新根,以t->lc和t为左、右孩子
t.parent = _root = new BinNode<Integer>(e,null,t.lc,t);
if (HasLChild(t)){
t.lc.parent = _root;
t.lc = null;
}
}
updateHeightAbove(t);
//更新t及其祖先(实际上只有_root一个)的高度
return _root;
//新节点必然置于树根,返回之
}
//删除算法
public Boolean remove(Integer e){
//从伸展树中删除关键码e
if ((_root == null) || (e != search(e).data)) return false;
//若树空或目标不存在,则无法删除
BinNode<Integer> w = _root;
//assert: 经search()后节点e已被伸展至树根
if (!HasLChild(_root)){
//若无左子树,则直接删除
_root = _root.rc;
if (_root != null) _root.parent = null;
} else if (!HasRChild(_root)){
//若无右子树,也直接删除
_root = _root.lc;
if (_root != null) _root.parent = null;
} else {
//若左右子树同时存在,则
BinNode<Integer> lTree = _root.lc;
lTree.parent = null;
_root.lc = null;
//暂时将左子树切除
_root = _root.rc;
_root.parent = null;
//只保留右子树
search(w.data);
//以原树根为目标,做一次(必定失败的)查找
_root.lc = lTree;
lTree.parent = _root;
// assert: 至此,右子树中最小节点必伸展至根,且(因无雷同节点)其左子树必空,于是只需将原左子树接回原位即可
}
if (_root != null){
updateHeight(_root);
//此后,若树非空,则树根的高度需要更新
}
return true;
//返回成功标志,若目标节点存在且被删除,返回true;否则返回false
}
当数据规模大到内存已不足以容纳时,常规平衡二叉搜索树的效率将大打折扣。其原因在于,查找过程对外存的访问次数过多。
通过时间成本相对极低的多次内存操作,来替代时间成本相对极高的单次外存操作。相应地,需要将通常的二叉搜索树,改造为多路搜索树
由于各节点的分支数介于[m/2]至m之间,故m阶B-树也称作([m/2], m)-树
例如,图8.12(a)即为一棵由9个内部节点、15个外部节点以及14个关键码组成的4阶B-树,其高度h = 3,其中每个节点包含13个关键码,拥有24个分支。
public class BTNode<T> {
BTNode<T> parent;
//父节点
Vec<T> key;
//关键码向量
Vec<BTNode<T>> child;
//孩子向量(其长度总比key多一)
public BTNode() {
// 构造函数(注意:BTNode只能作为根节点创建,而且初始时有0个关键码和1个空孩子指针)
parent = null;
child.add(0,null);
}
public BTNode(Integer e,BTNode<T> lc,BTNode<T> rc ) {
parent = null;
//作为根节点,而且初始时
key.add(0, (T) e);
//只有一个关键码,以及
child.add(0,lc);
//两个孩子
child.add(1,rc);
if (lc != null){
lc.parent = this;
}
if (rc != null){
rc.parent = this;
}
}
}
public class BTree<T> extends BTNode<T>{
protected int _size;
//存放的关键码总数
protected int _order;
//B-树的阶次,至少为3——创建时指定,一般不能修改
BTNode<T> _root;
//根节点
BTNode<T> _hot;
//BTree::search()最后访问的非空(除非树空)的节点位置
public BTree(int _size, int _order) {
this._size = _size;
this._order = _order;
_root = new BTNode<T>();
}
public BTree() {
_root = new BTNode<T>();
}
public int get_size() {
return _size;
}
public void set_size(int _size) {
this._size = _size;
}
public int get_order() {
return _order;
}
public void set_order(int _order) {
this._order = _order;
}
public BTNode<T> get_root() {
return _root;
}
public void set_root(BTNode<T> _root) {
this._root = _root;
}
public Boolean empty(){
//判空
return _root == null;
}
}
从根节点开始,通过关键码的比较不断深入至下一层,直到某一关键码命中(查找成功),或者到达某一外部节点(查找失败)
//查找算法
public BTNode<T> search(Integer e){
//在B-树中查找关键码e
BTNode<T> v = _root;
//从根节点出发
_hot = null;
while (v != null){
//逐层查找
int r = v.key.search(e);
//在当前节点中,找到不大于e的最大关键码
if ((0 <= r) && (e == v.key.elementAt(r))) return v;
//成功:在当前节点中命中目标关键码
_hot = v;
v = (BTNode<T>) v.child.elementAt(r +1);
//否则,转入对应子树(_hot指向其父)——需做I/O,最费时间
}
//这里在向量内是二分查找,但对通常的_order可直接顺序查找
return null;
//失败:最终抵达外部节点
}
效果:尽管没有渐进意义上的改进,但相对而言极其耗时的I/O操作的次数,却已大致缩减为原先的1/log2m。
//插入算法
public Boolean insert(Integer e){
//将关键码e插入B树中
BTNode<T> v = search(e);
//确认目标节点不存在
if (v != null) return false;
int r = _hot.key.search( e);
//在节点_hot的有序关键码向量中查找合适的插入位置
_hot.key.add(r+1, (T) e);
//将新关键码插至对应的位置
_hot.child.add(r+2,null);
//创建一个空子树指针
_size++;
//更新全树规模
solveOverflow(_hot);
//如有必要,需做分裂
return true;
}
上溢
//分裂算法
protected void solveOverflow(BTNode<T> v){
if (_order >= v.child.size()) return;
//递归基:当前节点并未上溢
int s = _order/2;
//轴点(此时应有_order = key.size() = child.size() - 1)
BTNode<T> u = new BTNode<T>();
//注意:新节点已有一个空孩子
for (int j = 0;j < _order - s - 1;j++){
//v右侧_order-s-1个孩子及关键码分裂为右侧节点u
u.child.add(j,v.child.remove(s + 1));
//逐个移动效率低
u.key.add(j,v.key.remove(s + 1));
//此策略可改进
}
u.child.set(_order - s -1,v.child.remove(s+1));
//移动v最靠右的孩子
if (u.child.elementAt(0) != null){
//若u的孩子们非空,则
for (int j = 0;j < _order - s;j++){
//令它们的父节点统一
u.child.elementAt(j).parent = u;
//指向u
}
}
BTNode<T> p = v.parent;
//v当前的父节点p
if (p == null){
//若p空则创建之
_root = p = new BTNode<T>();
p.child.set(0,v);
v.parent = p;
}
int r = 1 + p.key.search((Integer) v.key.elementAt(0));
//p中指向u的指针的秩
p.key.add(r,v.key.remove(s));
//轴点关键码上升
p.child.add(r + 1,u);
u.parent = p;
//新节点u与父节点p互联
solveOverflow(p);
//上升一层,如有必要则继续分裂——至多递归O(logn)层
}
template <typename T> //关键码删除后若节点下溢,则做节点旋转或合并处理
void BTree<T>::solveUnderflow ( BTNodePosi(T) v ) {
if ( ( _order + 1 ) / 2 <= v->child.size() ) return;
//递归基:当前节点并未下溢
BTNodePosi(T) p = v->parent;
if ( !p ) {
//递归基:已到根节点,没有孩子的下限
if ( !v->key.size() && v->child[0] ) {
//但倘若作为树根的v已不含关键码,却有(唯一的)非空孩子,则
_root = v->child[0];
_root->parent = NULL;
//这个节点可被跳过
v->child[0] = NULL;
release ( v );
//并因不再有用而被销毁
}
//整树高度降低一层
return;
}
Rank r = 0;
while ( p->child[r] != v ) r++;
//确定v是p的第r个孩子——此时v可能不含关键码,故不能通过关键码查找
//另外,在实现了孩子指针的判等器之后,也可直接调用Vector::find()定位
// 情况1:向左兄弟借关键码
if ( 0 < r ) {
//若v不是p的第一个孩子,则
BTNodePosi(T) ls = p->child[r - 1];
//左兄弟必存在
if ( ( _order + 1 ) / 2 < ls->child.size() ) {
//若该兄弟足够“胖”,则
v->key.insert ( 0, p->key[r - 1] );
//p借出一个关键码给v(作为最小关键码)
p->key[r - 1] = ls->key.remove ( ls->key.size() - 1 );
//ls的最大关键码转入p
v->child.insert ( 0, ls->child.remove ( ls->child.size() - 1 ) );
//同时ls的最右侧孩子过继给v
if ( v->child[0] ) v->child[0]->parent = v;
//作为v的最左侧孩子
return;
//至此,通过右旋已完成当前层(以及所有层)的下溢处理
}
}
//至此,左兄弟要么为空,要么太“瘦”
// 情况2:向右兄弟借关键码
if ( p->child.size() - 1 > r ) {
//若v不是p的最后一个孩子,则
BTNodePosi(T) rs = p->child[r + 1];
//右兄弟必存在
if ( ( _order + 1 ) / 2 < rs->child.size() ) {
//若该兄弟足够“胖”,则
v->key.insert ( v->key.size(), p->key[r] );
//p借出一个关键码给v(作为最大关键码)
p->key[r] = rs->key.remove ( 0 );
//ls的最小关键码转入p
v->child.insert ( v->child.size(), rs->child.remove ( 0 ) );
//同时rs的最左侧孩子过继给v
if ( v->child[v->child.size() - 1] ) //作为v的最右侧孩子
v->child[v->child.size() - 1]->parent = v;
return;
//至此,通过左旋已完成当前层(以及所有层)的下溢处理
}
}
//至此,右兄弟要么为空,要么太“瘦”
// 情况3:左、右兄弟要么为空(但不可能同时),要么都太“瘦”——合并
if ( 0 < r ) {
//与左兄弟合并
BTNodePosi(T) ls = p->child[r - 1];
//左兄弟必存在
ls->key.insert ( ls->key.size(), p->key.remove ( r - 1 ) );
p->child.remove ( r );
//p的第r - 1个关键码转入ls,v不再是p的第r个孩子
ls->child.insert ( ls->child.size(), v->child.remove ( 0 ) );
if ( ls->child[ls->child.size() - 1] ) //v的最左侧孩子过继给ls做最右侧孩子
ls->child[ls->child.size() - 1]->parent = ls;
while ( !v->key.empty() ) {
//v剩余的关键码和孩子,依次转入ls
ls->key.insert ( ls->key.size(), v->key.remove ( 0 ) );
ls->child.insert ( ls->child.size(), v->child.remove ( 0 ) );
if ( ls->child[ls->child.size() - 1] ) ls->child[ls->child.size() - 1]->parent = ls;
}
release ( v );
//释放v
} else {
//与右兄弟合并
BTNodePosi(T) rs = p->child[r + 1];
//右兄度必存在
rs->key.insert ( 0, p->key.remove ( r ) );
p->child.remove ( r );
//p的第r个关键码转入rs,v不再是p的第r个孩子
rs->child.insert ( 0, v->child.remove ( v->child.size() - 1 ) );
if ( rs->child[0] ) rs->child[0]->parent = rs;
//v的最左侧孩子过继给ls做最右侧孩子
while ( !v->key.empty() ) {
//v剩余的关键码和孩子,依次转入rs
rs->key.insert ( 0, v->key.remove ( v->key.size() - 1 ) );
rs->child.insert ( 0, v->child.remove ( v->child.size() - 1 ) );
if ( rs->child[0] ) rs->child[0]->parent = rs;
}
release ( v );
//释放v
}
solveUnderflow ( p );
//上升一层,如有必要则继续分裂——至多递归O(logn)层
return;
}
//删除算法
public Boolean remove(Integer e){
BTNode<T> v = search(e);
if (v == null) return false;
int r = v.key.search(e);
if (v.child.elementAt(0) != null){
BTNode<T> u = v.child.elementAt(r + 1);
while (u.child.elementAt(0) != null) u = u.child.elementAt(0);
v.key.setElementAt(u.key.elementAt(0),r);
v = u;
r = 0;
}
v.key.remove(r);
v.child.remove(r + 1);
_size--;
solveUnderflow(v);
return true;
}
//合并算法
protected void solveUnderflow(BTNode<T> v){
if((_order + 1) / 2 <= v.child.size()) return;
BTNode<T> p = v.parent;
if ( p == null){
if ((v.key.size() == 0) && (v.child.elementAt(0) != null)){
_root = v.child.elementAt(0);
_root.parent = null;
v.child.setElementAt(null,0);
}
return;
}
int r = 0;
while (p.child.elementAt(r) != v) r++;
if (0 < r){
BTNode<T> ls = p.child.elementAt(r - 1);
if ((_order + 1) / 2 < ls.child.size()){
v.key.add(0,p.key.elementAt(r-1));
p.key.setElementAt(ls.key.remove(ls.key.size() - 1),r-1);
v.child.add(0,ls.child.remove(ls.child.size() - 1));
if (v.child.get(0) != null) v.child.get(0).parent = v;
return;
}
}
if (r < (p.child.size() - 1)){
BTNode<T> rs = p.child.elementAt(r + 1);
if ((_order + 1) / 2 < rs.child.size()){
v.key.add(v.key.size(),p.key.elementAt(r));
p.key.setElementAt(rs.key.remove(0),r);
v.child.add(v.child.size(),rs.child.remove(0));
if (v.child.get(v.child.size() - 1) != null) v.child.get(v.child.size() - 1).parent = v;
return;
}
}
if (0 < r){
BTNode<T> ls = p.child.get(r - 1);
ls.key.add(ls.key.size(),p.key.remove(r - 1));
p.child.remove(r);
ls.child.add(ls.child.size(),v.child.remove(0));
if (ls.child.get(ls.child.size() - 1) != null){
ls.child.get(ls.child.size() - 1).parent = ls;
}
while (!v.key.isEmpty()){
ls.key.add(ls.key.size(),v.key.remove(0));
ls.child.add(ls.child.size(),v.child.remove(0));
if (ls.child.get(ls.child.size() - 1) != null){
ls.child.get(ls.child.size() - 1).parent = ls;
}
}
} else {
BTNode<T> rs = p.child.get(r+1);
rs.key.add(0,p.key.remove(r));
p.child.remove(r);
rs.child.add(0,v.child.remove(v.child.size() - 1));
if (rs.child.get(0) != null) rs.child.get(0).parent = rs;
while (!v.key.isEmpty()){
rs.key.add(0,v.key.remove(v.key.size() - 1));
rs.child.add(0,v.child.remove(v.child.size() - 1));
if (rs.child.get(0) != null) rs.child.get(0).parent = rs;
}
}
solveUnderflow(p);
return;
}
红黑树可保证:
在每次插入或删除操作之后的重平衡过程中,全树拓扑结构的更新仅涉及常数个节点。尽管最坏
情况下需对多达?(logn)个节点重染色,但就分摊意义而言仅为O(1)个
由红、黑两色节点组成的二叉搜索树若满足以下条件,即为红黑树
(1) 树根始终为黑色
(2) 外部节点均为黑色
(3) 其余节点若为红色,则其孩子节点必为黑色
(4) 从任一外部节点到根节点的沿途,黑节点的数目相等
package com.atguigu.self;
/**
* @anthor shkstart
* @create 2020-08-10 8:46
*/
/*
里直接沿用了二叉搜索树标准的查找算法search(),并根据红黑树的重平衡规则
与算法,重写了insert()和remove()接口;新加的两个内部功能接口solveDoubleRed()和
solveDoubleBlack(),分别用于在节点插入或删除之后恢复全树平衡。
*/
public class RedBlack<T> extends BST<T>{
public BinNode<T> insert(T e){
//插入(重写)
BinNode<T> x = search(e);
if (x != null) return x;
//确讣目标节点丌存在(留意对_hot的设置)
x = new BinNode<T>(e,_hot,null,null,-1);
//创建红节点x:以_hot为父,黑高度-1
_size++;
solveDoubleRed(x);
return x;
//经双红修正后,即可迒回
}
//无论e是否存在于原树中,返回时总有x->data == e
public Boolean remove(T e){
//从红黑树中删除关键码e
BinNode<T> x = search(e);
if (x == null) return false;
//确认目标节点存在(留意对_hot的设置)
BinNode<T> r = removeAt(x,_hot);
//实施删除,_hot某一孩子刚被初除,且被r所指节点(可能是NULL)接替。以下检查是否失衡,幵做必要调整
if (0 > --_size) return true;
if (_hot == null){
//若刚被删除的是根节点,则将其置黑,幵更新黑高度
_root.color = RBColor.RB_BLACK;
updateHeight(_root);
return true;
}
if (BlackHeightUpdated(_hot)) return true;
//若所有祖先的黑深度依然平衡,则无需调整
if (IsRed(r)){
//否则,若r为红,则叧需令其转黑
r.color = RBColor.RB_BLACK;
r.height++;
return true;
}
solveDoubleBlack(r);
//经双黑调整后返回
return true;
}
protected void solveDoubleRed(BinNode<T> x){
//双红修正
if (IsRoot(x)){
_root.color = RBColor.RB_BLACK;
_root.height++;
return;
}
BinNode<T> p = x.parent;
if (IsBlack(p)) return;
BinNode<T> g = p.parent;
BinNode<T> u = uncle(x);
/**
* 这等效于按中序遍历次序,对节点x、p和g及其四棵子树,做一次局部“3 + 4”重构。
*/
if (IsBlack(u)){
if (IsLChild(x) == IsLChild(p)){
p.color = RBColor.RB_BLACK;
} else {
x.color = RBColor.RB_BLACK;
}
g.color = RBColor.RB_RED;
BinNode<T> gg = g.parent;
BinNode<T> r = g;
if (IsRoot(g)){
r = _root = rotateAt(x);
} else {
if (IsLChild(g)){
r = g.parent.lc = rotateAt(x);
} else {
r = g.parent.rc = rotateAt(x);
}
}
r.parent = gg;
} else {
/**
* 红黑树的角度来看,只需将红节点p和u转为黑色,黑节点g转
* 为红色,x保持红色。从图(c‘)B-树的角度来看,等效于上溢节点的一次分裂。
*/
p.color = RBColor.RB_BLACK;
p.height++;
u.color = RBColor.RB_BLACK;
u.height++;
if (!IsRoot(g)){
g.color = RBColor.RB_RED;
}
solveDoubleRed(g);
}
}
/**
* 分为三大类共四种情冴:
* BB-1 :2次颜色翻转,2次黑高度更新,1~2次旋转,丌再逑弻
* BB-2R:2次颜色翻转,2次黑高度更新,0次旋转,丌再逑弻
* BB-2B:1次颜色翻转,1次黑高度更新,0次旋转,需要逑弻
* BB-3 :2次颜色翻转,2次黑高度更新,1次旋转,转为BB-1戒BB2R
* @param r
*/
protected void solveDoubleBlack(BinNode<T> r){
//双黑修正
BinNode<T> p = (r != null) ? r.parent : _hot;
if (p == null) return;
BinNode<T> s = (r == p.lc) ? p.rc:p.lc;
if (IsBlack(s)){
/**
* 下溢节点从父节点借出一个关键码(p),
* 然后父节点从向下溢节点的兄弟节点借出一个关键码(s),调整后的效果如图(b‘)。
*/
BinNode<T> t = null;
if (HasLChild(s) && IsRed(s.lc)) t = s.lc; else if (HasRChild(s) && IsRed(s.rc)) t = s.rc;
if (t != null){
RBColor oldColor = p.color;
BinNode<T> b = p;
if (IsRoot(p)){
b = _root = rotateAt(t);
} else {
if (IsLChild(p)){
b = p.parent.lc = rotateAt(t);
} else {
b = p.parent.rc = rotateAt(t);
}
}
b.color = oldColor;
updateHeight(b);
} else {
s.color = RBColor.RB_RED;
s.height--;
if (IsRed(p)){
/**
* 将关键码p取出并下降一层,然后以之为“粘合剂”,
* 将原左、右孩子合并为一个节点。从红黑树角度看,这
* 一过程可如图(b)所示等效地理解为:s和p颜色互换。
*/
p.color = RBColor.RB_BLACK;
} else {
/**
* 将下溢节点与其兄弟合并。从红黑树的角 度来看,这一过程可如图(b)所示等
* 效地理解为:节点s由黑转红。
*/
p.height--;
solveDoubleBlack(p);
}
}
} else {
/**
* 令关键码s与p互换颜色,即可得到一棵与之完全等价
* 的B-树。而从红黑树的角度来看,这一转换对应于以节点p为轴做一
* 次旋转,并交换节点s与p的颜色,接下来可以套用此前所介绍其它情况的处置方法,继
* 续并最终完成双黑修正
*/
s.color = RBColor.RB_BLACK;
p.color = RBColor.RB_RED;
BinNode<T> t = IsLChild(s) ? s.lc : s.rc;
_hot = p;
if (IsRoot(p)){
_root = rotateAt(t);
} else {
if (IsLChild(p)){
p.parent.lc = rotateAt(t);
} else {
p.parent.rc = rotateAt(t);
}
}
solveDoubleBlack(r);
}
}
/**
* 节点黑高度需要更新的情况共分三种:或者左、右孩子的黑高度不等;或者作为红节点,黑高度与其孩
* 子不相等;或者作为黑节点,黑高度不等于孩子的黑高度加一。
* @param x
* @return
*/
@Override
public int updateHeight(BinNode<T> x){
//更新红黑树节点高度
x.height = Math.max(stature(x.lc),stature(x.rc));
//孩子一般黑高度相等,除非出现双黑
return IsBlack(x) ? x.height++: x.height;
//若当前节点为黑,则计入黑深度
}
/*
检查节点的颜色以及判定是否需要更新(黑)高度记录,如此可大大简化相关算法的描述
*/
Boolean IsBlack(BinNode<T> p){
//外部节点也看作黑节点
return ((p == null) || (RBColor.RB_BLACK == p.color));
}
Boolean IsRed(BinNode<T> p){
//非黑即红
return !IsBlack(p);
}
Boolean BlackHeightUpdated(BinNode<T> x){
//RedBlack高度更新条件,红不变高度,黑增高一个
return (stature(x.lc) == stature(x.rc)) &&
(x.height == (IsRed(x) ? stature(x.lc) : stature(x.lc) + 1));
}
}
public BinNode<T> insert(T e){
//插入(重写)
BinNode<T> x = search(e);
if (x != null) return x;
//确讣目标节点丌存在(留意对_hot的设置)
x = new BinNode<T>(e,_hot,null,null,-1);
//创建红节点x:以_hot为父,黑高度-1
_size++;
solveDoubleRed(x);
return x;
//经双红修正后,即可迒回
}
//无论e是否存在于原树中,返回时总有x->data == e
protected void solveDoubleRed(BinNode<T> x){
//双红修正
if (IsRoot(x)){
_root.color = RBColor.RB_BLACK;
_root.height++;
return;
}
BinNode<T> p = x.parent;
if (IsBlack(p)) return;
BinNode<T> g = p.parent;
BinNode<T> u = uncle(x);
/**
* 这等效于按中序遍历次序,对节点x、p和g及其四棵子树,做一次局部“3 + 4”重构。
*/
if (IsBlack(u)){
if (IsLChild(x) == IsLChild(p)){
p.color = RBColor.RB_BLACK;
} else {
x.color = RBColor.RB_BLACK;
}
g.color = RBColor.RB_RED;
BinNode<T> gg = g.parent;
BinNode<T> r = g;
if (IsRoot(g)){
r = _root = rotateAt(x);
} else {
if (IsLChild(g)){
r = g.parent.lc = rotateAt(x);
} else {
r = g.parent.rc = rotateAt(x);
}
}
r.parent = gg;
} else {
/**
* 红黑树的角度来看,只需将红节点p和u转为黑色,黑节点g转
* 为红色,x保持红色。从图(c‘)B-树的角度来看,等效于上溢节点的一次分裂。
*/
p.color = RBColor.RB_BLACK;
p.height++;
u.color = RBColor.RB_BLACK;
u.height++;
if (!IsRoot(g)){
g.color = RBColor.RB_RED;
}
solveDoubleRed(g);
}
}
public Boolean remove(T e){
//从红黑树中删除关键码e
BinNode<T> x = search(e);
if (x == null) return false;
//确认目标节点存在(留意对_hot的设置)
BinNode<T> r = removeAt(x,_hot);
//实施删除,_hot某一孩子刚被初除,且被r所指节点(可能是NULL)接替。以下检查是否失衡,幵做必要调整
if (0 > --_size) return true;
if (_hot == null){
//若刚被删除的是根节点,则将其置黑,幵更新黑高度
_root.color = RBColor.RB_BLACK;
updateHeight(_root);
return true;
}
if (BlackHeightUpdated(_hot)) return true;
//若所有祖先的黑深度依然平衡,则无需调整
if (IsRed(r)){
//否则,若r为红,则叧需令其转黑
r.color = RBColor.RB_BLACK;
r.height++;
return true;
}
solveDoubleBlack(r);
//经双黑调整后返回
return true;
}
/**
* 分为三大类共四种情冴:
* BB-1 :2次颜色翻转,2次黑高度更新,1~2次旋转,丌再逑弻
* BB-2R:2次颜色翻转,2次黑高度更新,0次旋转,丌再逑弻
* BB-2B:1次颜色翻转,1次黑高度更新,0次旋转,需要逑弻
* BB-3 :2次颜色翻转,2次黑高度更新,1次旋转,转为BB-1戒BB2R
* @param r
*/
protected void solveDoubleBlack(BinNode<T> r){
//双黑修正
BinNode<T> p = (r != null) ? r.parent : _hot;
if (p == null) return;
BinNode<T> s = (r == p.lc) ? p.rc:p.lc;
if (IsBlack(s)){
/**
* 下溢节点从父节点借出一个关键码(p),
* 然后父节点从向下溢节点的兄弟节点借出一个关键码(s),调整后的效果如图(b‘)。
*/
BinNode<T> t = null;
if (HasLChild(s) && IsRed(s.lc)) t = s.lc; else if (HasRChild(s) && IsRed(s.rc)) t = s.rc;
if (t != null){
RBColor oldColor = p.color;
BinNode<T> b = p;
if (IsRoot(p)){
b = _root = rotateAt(t);
} else {
if (IsLChild(p)){
b = p.parent.lc = rotateAt(t);
} else {
b = p.parent.rc = rotateAt(t);
}
}
b.color = oldColor;
updateHeight(b);
} else {
s.color = RBColor.RB_RED;
s.height--;
if (IsRed(p)){
/**
* 将关键码p取出并下降一层,然后以之为“粘合剂”,
* 将原左、右孩子合并为一个节点。从红黑树角度看,这
* 一过程可如图(b)所示等效地理解为:s和p颜色互换。
*/
p.color = RBColor.RB_BLACK;
} else {
/**
* 将下溢节点与其兄弟合并。从红黑树的角 度来看,这一过程可如图(b)所示等
* 效地理解为:节点s由黑转红。
*/
p.height--;
solveDoubleBlack(p);
}
}
} else {
/**
* 令关键码s与p互换颜色,即可得到一棵与之完全等价
* 的B-树。而从红黑树的角度来看,这一转换对应于以节点p为轴做一
* 次旋转,并交换节点s与p的颜色,接下来可以套用此前所介绍其它情况的处置方法,继
* 续并最终完成双黑修正
*/
s.color = RBColor.RB_BLACK;
p.color = RBColor.RB_RED;
BinNode<T> t = IsLChild(s) ? s.lc : s.rc;
_hot = p;
if (IsRoot(p)){
_root = rotateAt(t);
} else {
if (IsLChild(p)){
p.parent.lc = rotateAt(t);
} else {
p.parent.rc = rotateAt(t);
}
}
solveDoubleBlack(r);
}
}
/**
* 节点黑高度需要更新的情况共分三种:或者左、右孩子的黑高度不等;或者作为红节点,黑高度与其孩
* 子不相等;或者作为黑节点,黑高度不等于孩子的黑高度加一。
* @param x
* @return
*/
@Override
public int updateHeight(BinNode<T> x){
//更新红黑树节点高度
x.height = Math.max(stature(x.lc),stature(x.rc));
//孩子一般黑高度相等,除非出现双黑
return IsBlack(x) ? x.height++: x.height;
//若当前节点为黑,则计入黑深度
}
其中各节点的关键码可能重复。不过,如此并不致于增加渐进的空间和时间复杂度:每个关键码至多重复一次,总体依然只需O(n)空间;尽管相对于常规二叉搜索树仅多出一层,但树高依然是O(logn)。
查找的过程中,在每一节点处,至多只需做一次(而不是两次)关键码的比较。完全对应于和等价于二分查找算法的版本不考虑中间值
一维
二维
原文:https://www.cnblogs.com/suit000001/p/13472147.html