ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

7.3 Splay树

2021-03-02 13:01:22  阅读:197  来源: 互联网

标签:lc parent data Splay 7.3 rc root 节点


参考博客: https://blog.csdn.net/hellochenlu/article/details/53022709
参考博客: https://blog.csdn.net/amoscykl/article/details/81589827

本章问题

1.splay逐层伸展到根和双层伸展有什么区别呢?

假设节点的个数为n,则旋转操作的总次数的为(n-1)+{(n-1)+(n-2)+(n-3)+…+1}=(n2+n-2)/2= Ω \Omega Ω(n2)
逐层伸展,对于规模为任意n的伸展树,只要按照关键码单调的次序,周期性的进行反复的查找,则无论总的访问次数m>>n有多大,就分摊的意义而言,每次访问都需要 Ω \Omega Ω(n)的时间
双层伸展优点
折叠效果:一旦访问坏节点,对应路径的长度将随即减半。
最坏情况不致持续发生!
单趟伸展操作,分摊O(logn)时间!

2.splay为什么要伸展到根,这样做有什么好处

因为数据局部性原理,这样可以加快后续的操作
数据局部性原理:
1.刚刚被访问的节点,即有可能在不久之后再次被访问到
2.将被访问的下一节点,极有可能就处于不久的之前被访问过的某个节点的附近

3.Splay双层伸展的具体描述

参考博客:https://blog.csdn.net/hellochenlu/article/details/53022709

双层伸展
构思精髓:向上追溯两层,而非一层
反复考察祖孙三代:g = parent§,p = parent(v),v
根据它们的相对位置,经两次旋转使得 v上升两层,成为(子)树根

节点v的位置的四种可能性img

zig-zag / zag-zig
与AVL树双旋完全等效
与逐层伸展别无二致。img

颠倒次序后,局部的细微差异,将彻底地改变整体…img

折叠效果:一旦访问坏节点,对应路径的长度将随即减半。
最坏情况不致持续发生!
单趟伸展操作,分摊O(logn)时间!img

要是v只有父亲,没有祖父呢?
此时必有parent(v) == root(T),且
每轮调整中,这种情况至多(在最后)出现一次
视具体形态,做单词旋转:zig®或者zag®img

4.Splay树中查找算法的作用

将最后一个被访问的节点伸展至根,此时仍然还是二叉搜索树,此时仍满足特点
是一棵空树或具有以下几种性质的树:
1.若左子树不空,则左子树上所有结点的值均小于它的根结点的值
2.若右子树不空,则右子树上所有结点的值均大于它的根结点的值
3.左、右子树也分别为二叉排序树
4.没有权值相等的结点。

5.Splay中插入算法怎样完成局部重构

在查找接口Splay::search()已集成了splay()伸展功能,故查找返回后,树根节点要么等于查找目标(查找成功),要么就是_hot,而且恰为拟插入节点的直接前驱或直接后继(查找失败)img
如上图所示,为将关键码e插至伸展树T中,首先调用伸展树查找接口Splay::search(e),查找该关键码(图(a))。于是,其中最后被访问的节点t,将通过伸展被提升为树根,其左、右子树分别记作T L 和T R (图(b))。
接下来,根据e与t的大小关系(不妨排除二者相等的情况),以t为界将T分裂为两棵子树。比如,不失一般性地设e大于t。于是,可切断t与其右孩子之间的联系(图©),再将以e为关键码的新节点v作为树根,并以t作为其左孩子,以T R 作为其右子树(图(d))。
v小于t的情况与此完全对称。

6.Splay删除节点时若左右子树都存在,为什么要暂时切断左子树

为从伸展树中删除节点,固然也可以调用二叉搜索树标准的节点删除算法BST::remove(),再通过双层伸展,将该节点此前的父节点提升至树根。然而,同样鉴于Splay::search()已集成了splay()伸展功能,且在成功返回后,树根节点恰好就是待删除节点。因此,亦不妨改用如下方法实现Splay::remove()接口。
img

如图所示,为从伸展树T中删除关键码为e的节点,首先亦调用接口Splay::search(e),查找该关键码,且不妨设命中节点为v(图(a))。于是,v将随即通过伸展被提升为树根,其左、右子树分别记作T L 和T R (图(b))。接下来,将v摘除(图©)。然后,在T R 中再次查找关键码e。尽管这一查找注定失败,却可以将T R 中的最小节点m,伸展提升为该子树的根。
得益于二叉搜索树的顺序性,此时节点m的左子树必然为空;同时,T L 中所有节点都应小于m(图(d))。于是,只需将T L 作为左子树与m相互联接,即可得到一棵完整的二叉搜索树(图(e))。如此不仅删除了v,而且既然新树根m在原树中是v的直接后继,故数据局部性也得到了利用。

7.3.1 Splay的ADT接口

操作功能
splay (BinNodePosi(T) v)将节点伸展到根
search( const T& e )查找节点
insert ( const T& e )插入节点
remove( const T& e )删除节点

7.3.2 Splay的类模板

#include "BST.h"
#include <iostream>
template <typename T> class Splay : public BST<T> {
    protected:
        BinNodePosi(T) splay (BinNodePosi(T) v);//将节点伸展到根
    public:
        BinNodePosi(T) & search( const T& e );//查找
        BinNodePosi(T) insert( const T& e );//插入
        bool remove ( const T& e );//删除
};

7.3.4 Splay的伸展算法

template <typename NodePosi> inline
void attachAsLChild ( NodePosi p, NodePosi lc ) { p->lc = lc; if(lc) lc->parent = p; }
//节点*p与*lc建立父(左)子关系
template <typename NodePosi> inline
void attachAsRChild ( NodePosi p, NodePosi rc ) { p->rc = rc; if(rc) rc->parent = p; }
//节点*p与*rc建立父(右)子关系

//伸展算法的实现
template <typename T>
BinNodePosi(T) Splay<T>::splay ( BinNodePosi(T) v ){
    if( !v ) return nullptr; BinNodePosi(T) p; BinNodePosi(T) g;
    while( ( p = v->parent ) && ( g = p->parent )){//祖父与父亲都安在,双层伸展
        BinNodePosi(T) gg = g->parent;
        if( v->data < p->data)//如果节点v是p的左子树
            if( p->data < g->data){//zig-zig 
                attachAsLChild ( g, p->rc ); attachAsLChild( p, v->rc );
                attachAsRChild ( p, g ); attachAsRChild( v, p );
                std::cout << "here0" << p->data << g->data<< std::endl;
            }else{//zig-zag
                std::cout << "here1" <<std::endl;
                attachAsLChild ( p, v->rc ); attachAsRChild( g, v->lc );
                attachAsLChild ( v, g ); attachAsRChild( v, p );
            }
        else 
            if( p->data >= g->data){//zag-zag
                std::cout << "here2" <<std::endl;
                attachAsRChild ( g, p->lc ); attachAsRChild( p, v->lc );
                attachAsLChild ( p, g ); attachAsLChild( v, p );
            }else{//zag-zig
                std::cout << "here3" <<std::endl;
                attachAsRChild ( p, v->lc ); attachAsLChild( g, v->rc );
                attachAsRChild ( v, g ); attachAsLChild( v, p );
        }
        if ( !gg ) v->parent = NULL;
        else
            ( g == gg->lc ) ? attachAsLChild( gg, v ) : attachAsRChild( gg, v );
        BST<T>::updateHeight ( g ); BST<T>::updateHeight( p ); BST<T>::updateHeight( v );
    }
    if ( p = v->parent ){//只有父亲安在
        if (  v->data < p->data) { attachAsLChild( p, v->rc ) ; attachAsRChild( v, p ); }
        else                     { attachAsRChild( p, v->lc ) ; attachAsLChild( v, p ); }
        BST<T>::updateHeight( p ); BST<T>::updateHeight( v );
    }
    v->parent = NULL; return v;
}

7.3.5 查找

//查找
template <typename T> BinNodePosi(T) & Splay<T>::search( const T& e ){
    BinNodePosi(T) p = searchIn( this->_root, e, this->_hot = nullptr );
    this->_root = splay ( p ? p : this->_hot );
    return this->_root;
}

7.3.6 插入

//插入
template <typename T> BinNodePosi(T) Splay<T>::insert ( const T& e ){
    if ( !this->_root ) { this->_size ++; return this->_root = new BinNode<T> ( e ); }//处理原树为空的退化情况
    if ( e == search( e )->data ) return this->_root;//已经有这个数的情况
    this->_size++; BinNodePosi(T) t = this->_root;
    if ( this->_root->data < e ){
        t -> parent = this->_root = new BinNode<T> ( e, nullptr, t, t->rc );
        if ( HasRChild ( *t )) { t->rc->parent = this->_root; t->rc = nullptr; }
    }else{
        t->parent = this->_root = new BinNode<T> ( e, nullptr, t->lc, t );
        if ( HasLChild ( *t )) { t->lc->parent = this->_root; t->lc = nullptr; }
    }
    BST<T>::updateHeightAbove ( t );
    return this->_root;
}

7.3.7 删除

template <typename T> bool Splay<T>::remove( const T& e ){
    if (!this->_root || e != search(e)->data)  return false; 
    auto t = this->_root;
    if (!HasLChild( *(this->_root))) { // 若无左子树,直接删除
        this->_root = this->_root->rc; // 以右子树为树根
        if (this->_root) { this->_root->parent = nullptr; } // 更新右子树父节点为null
    } else if (!HasRChild( *(this->_root))) { // 若无右子树,直接删除
        this->_root = this->_root->lc; // 以左子树为树根
        if (this->_root) { this->_root->parent = nullptr; } // 更新左子树父节点为null
    } else { // 左、右子树同时存在
        // 暂时切除左子树
        auto ltree = this->_root->lc;
        ltree->parent = nullptr;
        this->_root->lc = nullptr;

        // 只保留右子树
        this->_root = this->_root->rc;
        this->_root->parent = nullptr;
        
        // 以原树根为目标,做一次(必定失败的)查找
        search(t->data);

        // 至此,右子树中最小节点必伸展至根,且(因无雷同节点)其左子树必空,于是只需将原左子树接回原位即可
        this->_root->lc = ltree;
        ltree->parent = this->_root;
    }

    // 释放节点,更新规模
    dtl::release(t->data); dtl::release(t); 
    this->_size--;

    // 此后,若树非空,则树根的高度需要更新
    if (this->_root) {
        this->updateHeight(this->_root);
    }

    return true;
}

7.3.8 Splay.h

#include "BST.h"
#include <iostream>
template <typename T> class Splay : public BST<T> {
    protected:
        BinNodePosi(T) splay (BinNodePosi(T) v);//将节点伸展到根
    public:
        BinNodePosi(T) & search( const T& e );//查找
        BinNodePosi(T) insert( const T& e );//插入
        bool remove ( const T& e );//删除
};
//查找
template <typename T> BinNodePosi(T) & Splay<T>::search( const T& e ){
    BinNodePosi(T) p = searchIn( this->_root, e, this->_hot = nullptr );
    this->_root = splay ( p ? p : this->_hot );
    return this->_root;
}
//插入
template <typename T> BinNodePosi(T) Splay<T>::insert ( const T& e ){
    if ( !this->_root ) { this->_size ++; return this->_root = new BinNode<T> ( e ); }//处理原树为空的退化情况
    if ( e == search( e )->data ) return this->_root;//已经有这个数的情况
    this->_size++; BinNodePosi(T) t = this->_root;
    if ( this->_root->data < e ){
        t -> parent = this->_root = new BinNode<T> ( e, nullptr, t, t->rc );
        if ( HasRChild ( *t )) { t->rc->parent = this->_root; t->rc = nullptr; }
    }else{
        t->parent = this->_root = new BinNode<T> ( e, nullptr, t->lc, t );
        if ( HasLChild ( *t )) { t->lc->parent = this->_root; t->lc = nullptr; }
    }
    BST<T>::updateHeightAbove ( t );
    return this->_root;
}
//删除
template <typename T> bool Splay<T>::remove( const T& e ){
    if (!this->_root || e != search(e)->data)  return false; 
    auto t = this->_root;
    if (!HasLChild( *(this->_root))) { // 若无左子树,直接删除
        this->_root = this->_root->rc; // 以右子树为树根
        if (this->_root) { this->_root->parent = nullptr; } // 更新右子树父节点为null
    } else if (!HasRChild( *(this->_root))) { // 若无右子树,直接删除
        this->_root = this->_root->lc; // 以左子树为树根
        if (this->_root) { this->_root->parent = nullptr; } // 更新左子树父节点为null
    } else { // 左、右子树同时存在
        // 暂时切除左子树
        auto ltree = this->_root->lc;
        ltree->parent = nullptr;
        this->_root->lc = nullptr;

        // 只保留右子树
        this->_root = this->_root->rc;
        this->_root->parent = nullptr;
        
        // 以原树根为目标,做一次(必定失败的)查找
        search(t->data);

        // 至此,右子树中最小节点必伸展至根,且(因无雷同节点)其左子树必空,于是只需将原左子树接回原位即可
        this->_root->lc = ltree;
        ltree->parent = this->_root;
    }

    // 释放节点,更新规模
    dtl::release(t->data); dtl::release(t); 
    this->_size--;

    // 此后,若树非空,则树根的高度需要更新
    if (this->_root) {
        this->updateHeight(this->_root);
    }

    return true;
}

template <typename NodePosi> inline
void attachAsLChild ( NodePosi p, NodePosi lc ) { p->lc = lc; if(lc) lc->parent = p; }//节点*p与*lc建立父(左)子关系
template <typename NodePosi> inline
void attachAsRChild ( NodePosi p, NodePosi rc ) { p->rc = rc; if(rc) rc->parent = p; }//节点*p与*rc建立父(右)子关系

//伸展算法的实现
template <typename T>
BinNodePosi(T) Splay<T>::splay ( BinNodePosi(T) v ){
    if( !v ) return nullptr; BinNodePosi(T) p; BinNodePosi(T) g;
    while( ( p = v->parent ) && ( g = p->parent )){//祖父与父亲都安在,双层伸展
        BinNodePosi(T) gg = g->parent;
        if( v->data < p->data)
            if( p->data < g->data){//zig-zig 
                attachAsLChild ( g, p->rc ); attachAsLChild( p, v->rc );
                attachAsRChild ( p, g ); attachAsRChild( v, p );
                std::cout << "here0" << p->data << g->data<< std::endl;
            }else{//zig-zag
                std::cout << "here1" <<std::endl;
                attachAsLChild ( p, v->rc ); attachAsRChild( g, v->lc );
                attachAsLChild ( v, g ); attachAsRChild( v, p );
            }
        else 
            if( p->data >= g->data){//zag-zag
                std::cout << "here2" <<std::endl;
                attachAsRChild ( g, p->lc ); attachAsRChild( p, v->lc );
                attachAsLChild ( p, g ); attachAsLChild( v, p );
            }else{//zag-zig
                std::cout << "here3" <<std::endl;
                attachAsRChild ( p, v->lc ); attachAsLChild( g, v->rc );
                attachAsRChild ( v, g ); attachAsLChild( v, p );
        }
        if ( !gg ) v->parent = NULL;
        else
            ( g == gg->lc ) ? attachAsLChild( gg, v ) : attachAsRChild( gg, v );
        BST<T>::updateHeight ( g ); BST<T>::updateHeight( p ); BST<T>::updateHeight( v );
    }
    if ( p = v->parent ){//只有父亲安在
        if (  v->data < p->data) { attachAsLChild( p, v->rc ) ; attachAsRChild( v, p ); }
        else                     { attachAsRChild( p, v->lc ) ; attachAsLChild( v, p ); }
        BST<T>::updateHeight( p ); BST<T>::updateHeight( v );
    }
    v->parent = NULL; return v;
}

7.3.9 Splay测试

#include "splay.h"
#include <iostream>
using namespace std;

template<typename T> void returnValue(T& a)
{
    cout << "return_value: " << a << endl;
}
int main(){
    Splay<int> bt_test;

	bt_test.insertAsRoot(7);
	BinNodePosi(int) root = bt_test.root();
	int i;
	const int a[7] = { 0, 6, 5, 4, 3, 2, 1};
	for( i = 1; i < 7; ++i ){
		bt_test.insertAsLC( root, 7-i );
		root = root->lc;
	}

	//测试用例
	/* 								7
							6
						5
					4
				3
			2
		1
	*/
	bt_test.search(1);//先序遍历的搜索结果为1642357,与推算结果符合
	/* 	
			1						
							6			
						4		7
				2			5
			 		3
	*/
	bt_test.insert(0);//先序遍历的搜索结果为01642357,与推算结果符合
	/* 	
	0
			1						
							6			
						4		7
				2			5
			 		3
	*/
	//bt_test.remove(59);//删除
	bt_test.remove(1);//先序遍历的搜索结果为2043657,与推算结果符合
	/* 	
		2
	0				4						
				3			6			
						5		7
							
			 		
	*/
	void (* visit)(int& ) = &returnValue;
	bt_test.traverse(bt_test.root(),visit);
	return 0;
    
}

标签:lc,parent,data,Splay,7.3,rc,root,节点
来源: https://blog.csdn.net/ZXG20000/article/details/114282636

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有