Splay-tree poj 3580

SplayTree 伸展树:自调节的二叉搜索树,能把最近访问的节点移到根节点出。适用于各种实际应用,尤其适合实现cache和garbage collection算法。空间少,性能平摊也有竞争力,编程易实现。

优点:

1、  实现简单,比AVL、红黑树更简单。

2、  性能好,平均性能比得上其他平衡二叉搜索树。可能会比AVL慢点。常数因子的差距。

3、  没有其他的记录数据。

4、  可以适用于相同键的数据,平摊仍然是O(lgn)。类似于稳定排序算法。

缺点:

1、  高度可能会退化到线性,但是平摊仍然是O(lgn)。

2、  多线程的“只读”操作也会改变,需要额外的处理。

支持的操作:

1、访问、动态删除插入,其他平衡树也可以做到。

2、拆分(子树1小于子树2)、合并。

支持的区间操作:

1、砍树:把一个区间[x,y]的所有节点组织成一个子树。

    依次访问y+1和x-1,Splay操作需要改动,伸展到root或是root->left或是root->right。这点是下面的基本。

2、对一段区间都增加一个值value 

    采用线段树的延时策略,必要时才往子节点更新。

3、翻转一个区间(顺序反转)

4、对一个区间向右移动k步(转化成交换两个相邻的区间):实际上就是一段区间向右循环移动:

    砍出一个子树,在子树里面继续砍出子树,交换子树位置。

5、插入一段区间 

     也有Splay操作。

6、删除一段区间 

    简单,把区间砍成一棵子树。不要忘了Splay子树的父节点操作。

7、返回一段区间的最小值

   砍树,得到子树根节点的统计信息。

poj 3580 代码:

#include <iostream>
#include <cstdio>

using namespace std;

#define TREESIZE 		2000010
#define ARRAYSIZE 		100010
#define INTMAX 		(1<<30)

struct Node {
	//int id;
	int value, min, delta, size;
	bool rev;

	Node * parent, *child[2];
};
Node nodes[TREESIZE];
int curNode=0;
int seq[ARRAYSIZE];

Node *null, *root;

Node* AllocNode( int value )
{
	nodes[curNode].child[0] = nodes[curNode].child[1] = null;
	nodes[curNode].delta = 0;
	nodes[curNode].size = 1;
	nodes[curNode].rev = false;
	nodes[curNode].min = nodes[curNode].value = value;

	++ curNode;
	return &nodes[curNode-1];
}

void Update( Node* p )
{
	// child一定存在
	if ( p == null )
		return;
	p->min = std::min( p->value, std::min( p->child[0]->min, p->child[1]->min ) );
	p->size = p->child[0]->size +  p->child[1]->size + 1;
}

void InitTree()
{
	//标志不参与计数
	null = NULL;
	null = AllocNode(INTMAX);
	null->size = 0;

	//两个哨兵,参与计数
	//头结点哨兵
	root = AllocNode(INTMAX);
	null->child[0] = null->child[1] = root;
	root->parent = null;

	//尾节点哨兵
	root->child[1] = AllocNode(INTMAX);
	root->child[1]->parent = root;

	Update( root );
}

void Pushdown( Node * p )
{
	if ( p == null )
		return;
	if ( 0 != p->delta )
	{
		p->value += p->delta;
		p->min += p->delta; 	//good
		p->child[0]->delta += p->delta;
		p->child[1]->delta += p->delta;
		p->delta = 0;
	}
	if ( p->rev )
	{
		Node *l = p->child[0];
		p->child[0] = p->child[1];
		p->child[1] = l;
		p->rev = false;

		p->child[0]->rev = !p->child[0]->rev;
		p->child[1]->rev = !p->child[1]->rev;
	}
}

//[): beg-->end-1
Node* MakeTree( int *interval, int beg, int end  )
{
	if ( beg == end )
		return null;

	if ( beg+1 == end )
	{
		Node *p = AllocNode(interval[beg]);
		p->min = interval[beg];

		return p;
	}

	int mid = ( beg + end )>>1;
	Node *p, *l, *r;

	p = AllocNode( interval[mid] );
	p->size = end-beg;

	l = MakeTree( interval, beg, mid );
	r = MakeTree( interval, mid+1, end );
	p->child[0] = l;
	p->child[1] = r;
	l->parent = r->parent = p;

	//update
	Update( p );
	return p;
}

//旋转:注意把推迟的信息压入子女节点中
/*
 *  Select已经更新了推迟的标示
 */
void Rotation( Node *cur, int direct )
{
	Node *y = cur->child[!direct];

	Pushdown( y->child[0] );
	Pushdown( y->child[1] );
	Pushdown( cur->child[direct] );

	cur->child[!direct] = y->child[direct];
	cur->child[!direct]->parent = cur;

	y->parent = cur->parent;
	y->child[direct] = cur;

	if ( y->parent->child[0] == cur)
		y->parent->child[0] = y;
	else
		y->parent->child[1] = y;
	cur->parent = y;

	//problem
	/**
	 *  对cur进行更新,cur的子女节点可能有推迟的信息,首先需要对cur原来的左子树的子女节点进行pushdown操作
	 *  也需要对cur原来的右子女进行Pushdown操作
	 */
	Update( cur );
	Update( y );
	if ( root == cur )
		root = y;
}

//伸展到fp的子女节点
void Splay( Node *cur, Node * fp )
{
	/**
	 *  保证cur的推迟信息在旋转之前更新到子女节点中
	 *  这样,root-->cur路径没有任何推迟信息
	 */
	Pushdown( cur );
	while ( cur->parent != fp )
	{
		if ( fp == cur->parent->parent )
		{
			if ( cur->parent->child[0] == cur )
				Rotation( cur->parent, 1 );    //right-rotation
			else
				Rotation( cur->parent, 0 );
		}
		else if ( cur->parent->child[0] == cur && cur->parent->parent->child[0] == cur->parent )
		{
			Rotation( cur->parent->parent, 1 );
			Rotation( cur->parent, 1 );
		}
		else if ( cur->parent->child[1] == cur && cur->parent->parent->child[1] == cur->parent )
		{
			Rotation( cur->parent->parent, 0 );
			Rotation( cur->parent, 0 );
		}
		else if ( cur->parent->child[0] == cur )
		{
			Rotation( cur->parent, 1 );
			Rotation( cur->parent, 0 );
		}
		else {
			Rotation( cur->parent, 0 );
			Rotation( cur->parent, 1 );
		}
	}
	Update( fp );	
}

/**
 *  先更新推迟的标志,再查询,保证root到curNode都是最新的(没有推迟标志)
 */
void Select( int rank, Node * fp )
{
	//寻找第rank个元素,从1编号
	Node * curNode = root;
	Pushdown( curNode );
	while ( rank != curNode->child[0]->size+1 )
	{
		if ( rank <= curNode->child[0]->size )
			curNode = curNode->child[0];
		else {
			rank -= curNode->child[0]->size + 1;
			curNode = curNode->child[1];
		}
		Pushdown( curNode );
	}
	Splay( curNode, fp );
}

//add after pos
void InsertInterval( int pos, int *interval, int cnt )
{
	// 第pos个元素称为根节点
	Select( pos+1, null );
	// 第pos+1个元素成为根节点的右孩子
	Select( pos+2, root );

	//在root->child[1]的左孩子(为NULL)建立新子树
	Node *p = MakeTree( interval, 0, cnt );
	root->child[1]->child[0] = p;
	p->parent = root->child[1];

	//伸展的同时更新整个路径,一定要有
	Splay( root->child[1]->child[0], null );
}

void DeleteInterval( int left, int right )
{
	Select( left, null );
	Select( right+2, root );
	root->child[1]->child[0] = null;

	Splay( root->child[1], null );
}

void Add( int left, int right, int data )
{
	//砍树[left,right],注意有一个头结点哨兵
	Select( left, null );
	Select( right+2, root );
	root->child[1]->child[0]->delta += data;

	Splay( root->child[1]->child[0], null );
}

int GetMin( int left, int right )
{
	Select( left, null );
	Select( right+2, root );

	return root->child[1]->child[0]->min;
}

//可以推迟旋转
void Reverse( int left, int right )
{
	Select( left, null );
	Select( right+2, root );
	root->child[1]->child[0]->rev = !root->child[1]->child[0]->rev;

	Splay( root->child[1]->child[0], null );
}

void Revolve( int left, int right, int data )
{
	++ left;
	++ right;

	int len;
	Node *x, *y;
	
	len = right-left+1;
	data = data%len;
	if ( 0 == data )
		return;
	
	Select( left, null );
	x = root;

	Select( left-1, null );
	Select( right+1, root );

	//left->right-data; right-data+1-->right
	Select( right-data, root->child[1] );
	
	y = root->child[1]->child[0];
	x->child[0] = y->child[1];
	x->child[0]->parent = x;
	y->child[1] = null;

	Splay( x, null );
}

int main()
{
	int n, i, m, left, right, data, pos;
	char instruct[50];
	
	while ( scanf("%d", &n ) != EOF )
	{
		for ( i = 0; i < n; ++ i )
			scanf("%d", &seq[i] );

		InitTree();

		//在第一个元素(头结点哨兵)后面加入
		InsertInterval( 0, seq, n );
		printf("%d\n", root->value );
		
		scanf("%d", &m);

		for ( i = 0; i < m; ++ i )
		{
			scanf("%s", instruct );
			if ( 'A' == instruct[0] )
			{
				scanf("%d%d%d", &left, &right, &data );		
				Add( left, right, data );
			}
			else if ( 'I' == instruct[0] )
			{
				scanf("%d%d", &pos, &data );
				InsertInterval( pos, &data, 1 );
			}
			else if ( 'D' == instruct[0] )
			{
				scanf("%d", &pos );
				DeleteInterval( pos, pos );
			}
			else if ( 'M' == instruct[0] )
			{
				scanf("%d%d", &left, &right );
				printf("%d\n", GetMin( left, right ) );
			}
			else if ( 'E' == instruct[3] )
			{
				scanf("%d%d", &left, &right );
				Reverse( left, right );
			}
			else {
				scanf("%d%d%d", &left, &right, &data );
				Revolve( left, right, data );
			}
			//Print();
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值