伸展树

        伸展树,是一种二叉排序树。它可以保证从空树开始任意连续M次对树的操作最多花费O(MlogN)时间。虽然这种保证并不排除任意一次操作花费O(N)时间的可能,而且这样的界也也不如每次操作最坏情形的界O(logN)那么短,但是实际效果是一样的:不存在坏的输入序列。一般来说,当M次操作的序列总的最坏情形运行时间为O(MF(N))时,我们说它的摊还(amortized)运行时间为O(F(N))。

        伸展树相比AVL树不需要额外的平衡因子或者高度等信息,在访问符合一定分布的数据集时性能尤佳,而且实现自顶向下的伸展树相对简单。

一,基本的自底向上伸展树

       伸展操作主要是将最近访问的结点通过一系列的旋转置于树的顶端。

 在旋转的时候,可以分为三种情况:

1、zig情况。
    X是查找路径上我们需要旋转的一个非根节点。
    如果X的父节点是根,那么我们用下图所示的方法旋转X到根:


这和一个普通的单旋转相同。
2、zig-zag情况。
在这种情况中,X有一个父节点P和祖父节点G(P的父节点)。X是右子节点,P是左子节点,或者反过来。这个就是双旋转。
先是X绕P左旋转,再接着X绕G右旋转。
如图所示:


3、zig-zig情况。
    这和前一个旋转不同。在这种情况中,X和P都是左子节点或右子节点。
    先是P绕G右旋转,接着X绕P右旋转。
    如图所示:


下面是一个例子,旋转节点c到根上。 


二、基本伸展树操作:
1、插入:
    当一个节点插入时,伸展操作将执行。因此,新插入的节点在根上。
2、查找:
    如果查找成功(找到),那么由于伸展操作,被查找的节点成为树的新根。
如果查找失败(没有),那么在查找遇到NULL之前的那个节点成为新的根。也就是,如果查找的节点在树中,那么,此时根上的节点就是距离这个节点最近的节点。
3、查找最大最小:
        查找之后执行伸展。
4、删除最大最小:
a)删除最小:
    首先执行查找最小的操作。
这时,要删除的节点就在根上。根据二叉查找树的特点,根没有左子节点。
使用根的右子结点作为新的根,删除旧的包含最小值的根。
b)删除最大:
首先执行查找最大的操作。
删除根,并把被删除的根的左子结点作为新的根。
5、删除:
        将要删除的节点移至根。
        删除根,剩下两个子树L(左子树)和R(右子树)。
        使用DeleteMax查找L的最大节点,此时,L的根没有右子树。
        使R成为L的根的右子树。
        如下图示:


三、自顶向下的伸展树:
    在自底向上的伸展树中,我们需要求一个节点的父节点和祖父节点,因此这种伸展树难以实现。因此,我们可以构建自顶向下的伸展树。
    当我们沿着树向下搜索某个节点X的时候,我们将搜索路径上的节点及其子树移走。我们构建两棵临时的树──左树和右树。没有被移走的节点构成的树称作中树。在伸展操作的过程中:
1、当前节点X是中树的根。
2、左树L保存小于X的节点。
3、右树R保存大于X的节点。
开始时候,X是树T的根,左右树L和R都是空的。和前面的自下而上相同,自上而下也分三种情况:
1、zig:


 如上图,在搜索到X的时候,所查找的节点比X小,将Y旋转到中树的树根。旋转之后,X及其右子树被移动到右树上。很显然,右树上的节点都大于所要查找的节点。注意X被放置在右树的最小的位置,也就是X及其子树比原先的右树中所有的节点都要小。这是由于越是在路径前面被移动到右树的节点,其值越大。

2、zig-zig:

在这种情况下,所查找的节点在Z的子树中,也就是,所查找的节点比X和Y都小。所以要将X,Y及其右子树都移动到右树中。首先是Y绕X右旋,然后Z绕Y右旋,最后将Z的右子树(此时Z的右子节点为Y)移动到右树中。注意右树中挂载点的位置。


3、zig-zag:

在这种情况中,首先将Y右旋到根。这和Zig的情况是一样的。然后变成上图右边所示的形状。接着,对Z进行左旋,将Y及其左子树移动到左树上。这样,这种情况就被分成了两个Zig情况。这样,在编程的时候就会简化,但是操作的数目增加(相当于两次Zig情况)。
    最后,在查找到节点后,将三棵树合并。如图:


将中树的左右子树分别连接到左树的右子树和右树的左子树上。将左右树作为X的左右子树。重新最成了一所查找的节点为根的树。

下面是一个查找节点19的例子:


    在例子中,树中并没有节点19,最后,距离节点最近的节点18被旋转到了根作为新的根。节点20也是距离节点19最近的节点,但是节点20没有成为新根,这和节点20在原来树中的位置有关系。

代码:

#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
using namespace std;

typedef struct SplayTreeNode * SplayTree;
typedef struct SplayTreeNode
{
	int data;
	SplayTreeNode *lchild,*rchild;
} SplayTreeNode;

void BuildSplayTree(SplayTree *T)
{
	int item;
	cin>>item;
	if(item==-1)
	{
		*T = NULL;
	}
	else {
		*T = new SplayTreeNode;
		(*T)->data = item; 
		BuildSplayTree(&((*T)->lchild));
		BuildSplayTree(&((*T)->rchild));
	}
}

SplayTree SingleRotateWithLeft(SplayTree x)
{
	/* x has left child y */
	SplayTree y;
	y = x->lchild;
	x->lchild = y->rchild;
	y->rchild = x;
	return y;
}

SplayTree SingleRotateWithRight(SplayTree x)
{
	/* x has right child y */
	SplayTree y;
	y = x->rchild;
	x->rchild = y->lchild;
	y->lchild = x;
	return y;
}

SplayTree DoubleRotateWithLeft(SplayTree x)
{
	/* x has left child x->lchild  and also 
	x->lchild has a  right child x->lchild->right*/
	x->lchild = SingleRotateWithRight(x->lchild);
	return SingleRotateWithLeft(x);
}

SplayTree DoubleRotateWithRight(SplayTree x)
{
	/* x has right child x->rchild  and also 
	x->rchild has a  left child x->rchild->lchild*/
	x->rchild = SingleRotateWithLeft(x->rchild);
	return SingleRotateWithRight(x);
}

SplayTree Splay(int key,SplayTree X)
{
	/* simple top down  splay, not requiring key in the tree X
	what it does is as described below                      */
	SplayTreeNode N;
	SplayTree left,right;
	
	if (X == NULL)
		return X;
	
	N.lchild = N.rchild = NULL;
	
	left = right = &N;
	
	while (key != X->data) {
		if (key < X->data)
		{
			if (X->lchild == NULL) 
				break;
			if( key < X->lchild->data) {
				/*小于左支路上连续的两个结点*/ 
				X = SingleRotateWithLeft(X);
			}
			/*旋转之后看停止条件是否满足*/
			if (X->lchild == NULL)
					break;
			right->lchild = X;
			right = X;
			X = X->lchild;
		}
		else {
			if ( X->rchild == NULL)
				break;
			if ( key > X->rchild->data) {
				/*大于右支路上连续的两个结点*/ 
				X = SingleRotateWithRight(X);				 
			}
			/*旋转后看是否满足停止条件*/ 
			if (X->rchild == NULL)
					break;
			left->rchild = X;
			left = X;
			X = X->rchild;
		
		}
	}
	left->rchild = X->lchild;
	right->lchild = X->rchild;
	X->lchild = N.rchild;
	X->rchild = N.lchild;
	return X;
} 

bool SplaySearch(int key, SplayTree *T)
{
	SplayTree t = *T;
	while (t != NULL) {
		if (key < t->data)
			t = t->lchild;
		else if (key > t->data)
			t = t->rchild;
		else {
			*T = Splay(key,*T);
			return true;
		}	
	}	
	return false;
} 

SplayTree SplayInsert(int key,SplayTree T)
{
	SplayTree t;
	t = new SplayTreeNode;
	t->data = key;
	t->lchild = t->rchild = NULL; 
	if (T == NULL) {
		T = t;
	}
	else {
		T = Splay(key,T);
		if (key < T->data) {
			t->lchild = T->lchild;
			t->rchild = T;
			T->lchild = NULL;
			T = t;
		}
		else {
			if (T->data <key) {
				t->rchild = T->rchild;
				t->lchild = T;
				T->rchild = NULL;
				T = t;
			}
			else {
				delete t;
			}
		}
	} 
	return T;	
		
}

SplayTree SplayDelete(int key, SplayTree T)
{
	SplayTree t;
	if (T != NULL) {
		
		T = Splay(key, T);
		
		if (key == T->data) {
			if (T->lchild == NULL)
				t = T->rchild;
			else {
				t = T->lchild;
				t = Splay(key,t);
				t->rchild = T->rchild;
			}
			delete T;
			T = t;
		}
	}
	return T;
}

inline int max(int a,int b)
{
	return a>b?a:b;
}
int Height(SplayTree T)
{
	if (T == NULL)
		return 0;
	else
		return 1 + max(Height(T->lchild), Height(T->rchild));
}
void MakeMat(SplayTree T,int root_x,int root_y,int step,int **m)
{
	int lChildPos,rChildPos;
	lChildPos = root_x - step;
	rChildPos = root_x + step;
	if (T == NULL) 
		return;
	else
	{
		m[root_y][root_x] = 1;
		MakeMat(T->lchild,lChildPos,root_y+1,step>>1,m);
		MakeMat(T->rchild,rChildPos,root_y+1,step>>1,m);
	}
}

void SplayTreeDisplay(SplayTree T)
{
	if(T == NULL)
		return;
	/* init placehold flags m[h][len] */
	int h = Height(T);
	int len = (1<<h) - 1;
	int row = h; 
	int **m = new int*[row];
	for(int i= 0;i<row;i++){
		m[i] = new int[len];
		memset(m[i],0,len*sizeof(int));	
	}
	/* get level order traversal sequence */
	vector<SplayTree> v;
	queue<SplayTree> q;
	queue<SplayTree> qt;
	q.push(T);
	SplayTree pt;
	while(!q.empty())
	{
		pt = q.front();
		if (pt->lchild != NULL)
			q.push(pt->lchild);
		if(pt->rchild != NULL)
			q.push(pt->rchild);
		v.push_back(pt);		
		q.pop();
	}
	/* generate output matrix  plus '/' and '\\' m[2*h-1][len] */
	MakeMat(T,len>>1,0,len+1>>2,m);
	/* generate output */
	int cnt = 0;
	int width = 1;
	for(int i = 0; i < row; i++)
	{
		for(int j = 0; j < len; j++)
		{
			if(m[i][j])
			{
//				if (i & 1) 
//					cout<<setw(width)<<char(m[i][j]);
//				else
//					cout<<setw(width)<<char(m[i][j]);
//					cout<<setw(width)<<m[i][j];
				cout<<(v[cnt])->data;
				cnt++;
			}
			else
				cout<<setw(width)<<' ';		
		}
		cout<<endl;
	}
	
}

int main()
{
	SplayTree T = NULL;
	int i;
	//BuildSplayTree(&T); 
	//SplayTreeDisplay(T);
	int a[] = {4,5,3,1,7,2,6};
 	for(i = 0; i < 6; i++) {
		cout<<"Key = "<<a[i]<<" inserting: "<<endl;
		T = SplayInsert(a[i],T);
		SplayTreeDisplay(T);
		cout<<"--------------------------------------------"
		"--------------------"<<endl;
	}
	
	for(i = 0; i < 6; i++) {
		cout<<"Key = "<<a[i]<<" searching: "<<endl;
		SplaySearch(a[i],&T);
		SplayTreeDisplay(T);
		cout<<"--------------------------------------------"
		"--------------------"<<endl;
	}
	for(i = 0; i < 6; i++) {
		cout<<"Key = "<<a[i]<<" deleting: "<<endl;
		T = SplayDelete(a[i],T);
		SplayTreeDisplay(T);
		cout<<"--------------------------------------------"
		"--------------------"<<endl;
	}
	
	cout<<"Key = "<<19<<" deleting: "<<endl;
	T = SplayDelete(19,T);
	SplayTreeDisplay(T);
	cout<<"--------------------------------------------"
		"--------------------"<<endl;
} 



测试输出:

Key = 4 inserting: 
4
----------------------------------------------------------------
Key = 5 inserting: 
 5 
4  
----------------------------------------------------------------
Key = 3 inserting: 
   3   
     4 
      5
----------------------------------------------------------------
Key = 1 inserting: 
       1       
           3   
             4 
              5
----------------------------------------------------------------
Key = 7 inserting: 
       7       
   5           
 3             
1 4            
----------------------------------------------------------------
Key = 2 inserting: 
       2       
   1       5   
         3   7 
          4    
----------------------------------------------------------------
Key = 4 searching: 
   4   
 2   5 
1 3   7
----------------------------------------------------------------
Key = 5 searching: 
       5       
   4       7   
 2             
1 3            
----------------------------------------------------------------
Key = 3 searching: 
       3       
   2       4   
 1           5 
              7
----------------------------------------------------------------
Key = 1 searching: 
                               1                               
                                               2               
                                                       3       
                                                           4   
                                                             5 
                                                              7
----------------------------------------------------------------
Key = 7 searching: 
       7       
   2           
 1   4         
    3 5        
----------------------------------------------------------------
Key = 2 searching: 
       2       
   1       7   
         4     
        3 5    
----------------------------------------------------------------
Key = 4 deleting: 
   3   
 2   7 
1   5  
----------------------------------------------------------------
Key = 5 deleting: 
   3   
 2   7 
1      
----------------------------------------------------------------
Key = 3 deleting: 
 2 
1 7
----------------------------------------------------------------
Key = 1 deleting: 
 2 
  7
----------------------------------------------------------------
Key = 7 deleting: 
2
----------------------------------------------------------------
Key = 2 deleting: 
----------------------------------------------------------------
Key = 19 deleting: 
----------------------------------------------------------------


REF:

1,http://www.cnblogs.com/kernel_hcy/archive/2010/03/17/1688360.html

2,数据结构与算法分析--C语言描述 Mark Allen Weiss

3,http://digital.cs.usu.edu/~allan/DS/Notes/Ch22.pdf

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要使 ListView 像一样可以自由伸展收缩,您可以使用带有分层结构的布局管理器,例如 ExpandableListView。ExpandableListView 是一种特殊的 ListView,它允许您在列表中展示分层结构数据,并且用户可以展开或折叠每个分组。 要使用 ExpandableListView,您需要创建一个继承自 ExpandableListAdapter 的自定义适配器,并将该适配器设置为 ExpandableListView 的适配器。您还需要提供数据源以及如何在列表中呈现该数据的视图。最后,您可以使用 ExpandableListView 的 setOnGroupClickListener() 和 setOnChildClickListener() 方法来处理用户单击分组或子项时的事件。 这是一个简单的示例,展示了如何使用 ExpandableListView 创建一个可以自由伸展收缩的列表: ``` // 创建自定义适配器 class MyExpandableListAdapter extends BaseExpandableListAdapter { // 提供数据源和呈现数据的视图 // 省略其他代码 } // 在布局文件中声明 ExpandableListView <ExpandableListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> // 在 Activity 中设置适配器并处理事件 ExpandableListView listView = findViewById(R.id.list_view); MyExpandableListAdapter adapter = new MyExpandableListAdapter(data); listView.setAdapter(adapter); listView.setOnGroupClickListener(new OnGroupClickListener ### 回答2: 如果需要让ListView像一样可以自由伸展收缩,可以使用ExpandableListView来实现。ExpandableListView是Android提供的一种可展开和折叠子列表的视图组件。 首先,在布局文件中,将原本的ListView替换为ExpandableListView,并为其指定唯一的id,例如: ``` <ExpandableListView android:id="@+id/expandableListView" ...> </ExpandableListView> ``` 然后,在代码中,创建一个适配器Adapter,继承BaseExpandableListAdapter,并重写相关的方法。这些方法包括getGroupCount()、getChildrenCount()、getGroup()、getChild()等。在这些方法里,可以根据需要编写逻辑来返回父项和子项的数据。 接着,设置适配器给ExpandableListView。例如: ``` ExpandableListView expandableListView = findViewById(R.id.expandableListView); expandableListView.setAdapter(adapter); ``` 最后,可以通过设置监听器来实现展开和折叠的功能。例如,可以为ExpandableListView设置OnGroupClickListener和OnChildClickListener来监听父项和子项的点击事件,然后在相应的回调方法中编写展开和折叠的代码。 总结起来,使用ExpandableListView可以实现ListView像一样自由伸展收缩的效果。通过自定义适配器来管理父项和子项的数据,并使用监听器来监控点击事件以展开和折叠列表项。这样,用户就可以像操作形结构一样,自由地展开和收缩ListView的项。 ### 回答3: 要实现一个可以自由伸展和收缩的ListView效果,可以使用ExpandableListView来达到这个目的。ExpandableListView是Android提供的一个可以展开和折叠的列表视图组件,可以让列表项像一样展开和收缩。 首先,需要在布局文件中将ListView替换为ExpandableListView组件。然后,创建一个ExpandableListAdapter来管理列表项的展开和收缩状态。 在实现ExpandableListAdapter时,需要重写以下几个方法: 1. getGroupCount()方法用于返回分组项的数量。 2. getChildrenCount()方法用于返回指定分组项下的子项数量。 3. getGroupView()方法用于获取分组项视图。 4. getChildView()方法用于获取子项视图。 在getGroupView()方法中,可以设置一个点击事件监听器,当用户点击分组项时,根据当前的状态展开或收缩子项。可以使用ExpandableListView的expandGroup()和collapseGroup()方法分别展开和收缩指定位置的分组。 通过以上步骤,就可以实现一个可以自由伸展和收缩的ExpandableListView。用户可以点击分组项来展开或收缩子项,从而实现类似结构的效果。 总结:使用ExpandableListView可以很方便地实现一个类似的列表视图,用户可以自由地展开和收缩分组项以及其对应的子项。这样可以提供更好的用户体验,并方便用户查看和管理大量的列表数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值