使用sbt容量平衡树实现shuffle

这篇文章是来源于去年同事询问的一个问题.  同事需要将数据库中的一些敏感信息实现遮蔽.  由于客户要求的规则比较复杂, 所以无法使用IBM现有的optim软件实现. 于是他自己写程序来操作. 当时我提议是否可以利用简单的变换,如将序号中数字顺序打乱,例如 123456改变为341526, 使用固定的方法,调整第n位的数字放置到第m位, 而且n,m由程序随机来生成,这样一来经过程序变换后,就无法和原来信息对应了. 可客户认为这种方法依然不好, 他们希望尽可能的随机打乱. 由于赶时间, 就告诉同事使用类似下面的方法实现了.

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>

using namespace std;

int main()
{
  vector<int> array ;
  for(int i=0;i<1000000;i++)
  {
    array.push_back(i);
  }

  srand(time(NULL));

  for (int i=0;i<1000000;i++)
  {
    int j=rand()%array.size();
    cout<<array[j]<<endl;
    array.erase(array.begin()+j);
  }
}    


上面的方法显然并不好. 每次取出1个数据后,vector收缩, 容易分析出消耗时间和数据总数的平方成正比. 如果vector不收缩,使用标记,某个数据被取出了, 下次遇到时,再次挑选.直到选出新的.按照这个思路就有了下面2种方法.

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>

using namespace std;

struct item
{
  int key;
  int flag;
};

int main()
{
  vector<item> array ;
  for(int i=0;i<1000000;i++)
  {
    item x;
    x.key=i;
    x.flag=1; 
    array.push_back(x);
  }

  int times=0;
  srand(time(NULL));

  for (int i=0;i<1000000;i++)
  {
    int m=rand()%array.size();
    times++;
    if(array[m].flag)
    {
      cout<<array[m].key<<endl;
      array[m].flag=0;
    }
    else
    {
      while(true)
      {
        int n=rand()%array.size();
        times++;
        if(array[n].flag)
        {
          cout<<array[n].key<<endl;
          array[n].flag=0;
          break;
        }
      }
    }
  }
  cerr<<"the total times "<<times<<endl;
}    


 

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>

using namespace std;

struct item
{
  int key;
  int flag;
};

int main()
{
  vector<item> array ;
  for(int i=0;i<1000000;i++)
  {
    item x;
    x.key=i;
    x.flag=1; 
    array.push_back(x);
  }

  int times=0;
  srand(time(NULL));

  for (int i=0;i<1000000;i++)
  {
    int m=rand()%array.size();
    times++;
    if(array[m].flag)
    {
      cout<<array[m].key<<endl;
      array[m].flag=0;
    }
    else
    {
      for(int j=1;j<1000000;j++)
      {
        m++;
        times++;
        if(m==1000000)
          m=0;
        if(array[m].flag)
        {
          cout<<array[m].key<<endl;
          array[m].flag=0;
          break;
        }
      }
    }
  }
  cerr<<"total times "<<times<<endl;
}


后来有时间仔细分析,认为实际是典型的shuffle问题. 但当时自己的思路认为上面的问题类似于再一个结构中,需要随机选择抽取数据, 如何比较快的抽取到相应的数据,而且整体结构的改变次数少,就可以加快速度. 由于自己做数据库,对于树相对熟悉,就自然而然的想到使用平衡树来解决. 而随机抽取某个位置的数据,实际是查找符合条件的数. 思考后认为需要在树的节点中包含子树节点总数的信息,这样一来就可以比较快的找到顺序中的第n个数据. 从网上查了一下,这符合sbt树.于是根据陈启峰的论文方法修改,写了下面程序.

(我当时简单证明了一下有的维护步骤是不需要的, 但我没有严格推导,为了预防错误,所以程序中加入了校核部分.)

#include <iostream>
#include <stdlib.h>
#include <stack>
#include <vector>

#include "sbt.h"

using namespace std;

//建立左旋转函数,逆时针旋转
//实际是将原来右侧子节点上升,原来节点下降,变换为新节点的左侧分支
void left_rotation(sbt_node *node)
{
	//正常BST左旋操作
	sbt_node *tmp;
	int tmp_key;
	tmp=node->left;
	tmp_key=node->key;
	node->left=node->right;
	node->key=node->right->key;
	node->right=node->left->right;
	node->left->right=node->left->left;
	node->left->left=tmp;
	node->left->key=tmp_key;

	//变换后,总体节点数不变.
	//但新的左侧分支节点数需要重新计算
	node->left->size=1;
	if (node->left->left!=NULL)
		node->left->size += node->left->left->size;
	if(node->left->right!=NULL)
		node->left->size +=node->left->right->size;
}

//建立右旋转函数,顺时针旋转
//与上面相对,左侧子节点上升,节点下降,变换为新节点的右侧分支
void right_rotation(sbt_node *node)
{
	//正常BST右旋操作
	sbt_node *tmp;
	int tmp_key;
	tmp=node->right;
	tmp_key=node->key;
	node->right=node->left;
	node->key=node->left->key;
	node->left=node->right->left;
	node->right->left=node->right->right;
	node->right->right=tmp;
	node->right->key=tmp_key;

	//变换后,总体节点数不变.
	//但新的右侧分支节点数需要重新计算
	node->right->size=1;
	if(node->right->left!=NULL)
		node->right->size+=node->right->left->size;
	if(node->right->right!=NULL)
		node->right->size+=node->right->right->size;
}

//返回key所在节点,如果找不到对应节点,则返回NULL
sbt_node *sbt_find(sbt_node *node,int key)
{
	while(node!=NULL)
	{
		if(key < node->key)
		{
			node=node->left;
		}
		else if(key == node->key)
		{
			break;
		}
		else
		{
			node=node->right;
		}
	}
	return node;
}

//返回按照从小到大排列,开始位置为1,第rank_level位置的节点
sbt_node *sbt_rank(sbt_node *node,int rank_level)
{
	//如果rank_level大于树的总节点数,实际下面循环后node为NULL,
	//所以不需要单独判断
	while(node!=NULL)
	{
		if (node->left!=NULL && node->left->size >= rank_level)
		{
			node=node->left;
		}
		else if((node->left!=NULL && node->left->size+1 == rank_level) || (node->left==NULL && rank_level==1))
		{
			break;
		}
		else
		{
			if(node->left!=NULL)
			{
				rank_level=rank_level - node->left->size - 1; //得到在子树中的位置
			}
			else
				rank_level--;                                 //此时无左侧分支,所以仅-1

			node=node->right;
		}
	}
	return node;
}

//按照从小到大排列顺序,开始位置为1,返回key在序列中的位置,key可能不是树中的节点值
//即此位置的元素小于等于key
int order_position(sbt_node *node,int key)
{
	int i=0;
	while(node!=NULL)
	{
		if(key < node->key)
		{
			node=node->left;
		}
		else if(key == node->key)
		{
			if(node->left!=NULL)               //判断有无左分支
			{
				i=i+ node->left->size +1;
			}
			else
			{
				i++;
			}
			break;
		}
		else
		{
			if(node->left!=NULL)
			{
				i=i + node->left->size +1;
			}
			else
			{
				i++;
			}
			node=node->right;
		}
	}
	return i;
}

//按照从小到大排列顺序,查找节点前一个位置的节点
//若此node是树中最小的节点,则返回NULL
sbt_node *sbt_prev(sbt_node *root,sbt_node *node)
{
	if(node->left!=NULL)
	{
		//如果node的左侧分支存在,则左子树的最右侧节点就是需要的节点
		node=node->left;
		while (node->right!=NULL)
		{
			node=node->right;
		}
		return node;
	}
	//从root逐步找到node,中间经过的节点入栈,
	//逐个出栈,直到node节点属于当前节点的右侧分支.
	//此时当前节点为需要的节点
	//stack<sbt_node *,vector<sbt_node *> > scan_nodes;   //使用vector模板操作是否会提高性能,能否使用预先分配栈空间大小的方法
	stack<sbt_node *> scan_nodes;
	while(root!=node)  //要求node必须是树的节点,否则这里将出现异常
	{
		scan_nodes.push(root);
		// 		assert(root!=NULL); //这里实际上可以用于判断node是否为树的节点
		if(root->key > node->key)
		{
			root=root->left;
		}
		else
		{
			root=root->right;
		}
	}
	//若此node是树中最小的节点,则返回NULL
	while( !scan_nodes.empty())
	{
		//逐个出栈,直到node节点属于当前出栈节点的右侧分支.
		root=scan_nodes.top();
		scan_nodes.pop();
		if(root->key < node->key)
		{
			return root;
		}
	}
	//此时node时root,而且无左分支,node为最小节点
	return NULL;
}

//按照从小到大排列顺序,查找节点后一个位置的节点
//若此node是树中最大的节点,则返回NULL
sbt_node *sbt_next(sbt_node *root,sbt_node *node)
{
	if(node->right!=NULL)
	{
		//如果node的右侧分支存在,则右子树的最左侧节点就是需要的节点
		node=node->right;
		while (node->left!=NULL)
		{
			node=node->left;
		}
		return node;
	}
	//从root逐步找到node,中间经过的节点入栈,
	//逐个出栈,直到node节点属于当前节点的左侧分支.
	//此时当前节点为需要的节点
	stack<sbt_node *> scan_nodes;
	while(root!=node)  //要求node必须是树的节点,否则这里将出现异常
	{
		scan_nodes.push(root);
		// 		assert(root!=NULL); //这里实际上可以用于判断node是否为树的节点
		if(root->key > node->key)
		{
			root=root->left;
		}
		else
		{
			root=root->right;
		}
	}
	//若此node是树中最大的节点,则返回NULL
	while( !scan_nodes.empty())
	{
		//逐个出栈,直到node节点属于当前出栈节点的左侧分支.
		root=scan_nodes.top();
		scan_nodes.pop();
		if(root->key > node->key)
		{
			return root;
		}
	}
	//此时node是root,而且无右分支,为最大节点
	return NULL;
}

//寻找最小的节点
sbt_node *sbt_min(sbt_node *node)
{
	while(node->left!=NULL)
	{
		node=node->left;
	}
	return node;
}

//寻找最大的节点
sbt_node *sbt_max(sbt_node *node)
{
	while(node->right!=NULL)
	{
		node=node->right;
	}
	return node;
}

//插入过程中,先搜索到合适的位置,插入节点,
//随后逐级向上平衡
//返回key所在节点,分配内存失败时返回NULL
sbt_node *sbt_insert(sbt_node *node,int key)
{
	if(node==NULL)
	{
		//当前为起始节点
		node=(sbt_node *)malloc(sizeof(sbt_node));
		if(node==NULL)
			return NULL;  //分配内存失败,返回NULL
		node->left=NULL;
		node->right=NULL;
		node->key=key;
		node->size=1;
		return node;
	}
	//查找后,在适当位置插入,如果对应的key已经存在,则直接返回对应的节点,不执行插入
	//查找的搜索路径上的节点逐个入栈,同时路径上的每个节点size+1
	//插入后,逐个出栈, 调用出栈节点的平衡过程
	sbt_node *temp=sbt_find(node,key);
	if(temp!=NULL)
	{
		return temp;  //可以找到key,所以直接返回对应节点
	}

	stack<sbt_node *> scan_nodes;
	while(node!=NULL)
	{
		node->size++;           //沿途上节点size+1
		scan_nodes.push(node);
		if(key < node->key)
		{
			node=node->left;
		}
		else
		{
			node=node->right;
		}
	}
	sbt_node *new_node=(sbt_node *)malloc(sizeof(sbt_node));
	if(new_node==NULL)
		return NULL;
	new_node->left=NULL;
	new_node->right=NULL;
	new_node->key=key;
	new_node->size=1;

	node=scan_nodes.top();     //此时scan_nodes一定非空
	if(node->key > key)
	{
		node->left=new_node;
	}
	else
	{
		node->right=new_node;
	}

	while(!scan_nodes.empty())
	{
		node=scan_nodes.top();
		if(node->key > new_node->key)
		{
			maintain(node,true);   //实际在左侧分支插入,所以需要检查右侧是否符合size的要求
		}
		else
		{
			maintain(node,false);  //实际在右侧分支插入,所以需要检查左侧是否符合size的要求
		}
		new_node=node;
		scan_nodes.pop();
	}

	//由于new_node在旋转过程中,可能改变为其他值,所以重新查找
	return sbt_find(node,key);
}

//删除,先搜索到对应的节点,
//逐级向上平衡
//成功返回0,树中不存在对应key的节点,则返回-1
int sbt_delete(sbt_node *node,int key)
{
	//搜索到对应的节点,将沿途节点入栈
	sbt_node *temp=sbt_find(node,key);
	if(temp==NULL)
	{
		//树中不存在key,所以返回-1
		return -1;
	}

	//沿途节点入栈,同时size-1
	stack<sbt_node *> scan_nodes;
	while(node!=temp)
	{
		node->size--;           //沿途节点size-1
		scan_nodes.push(node);
		if(key < node->key)
		{
			node=node->left;
		}
		else
		{
			node=node->right;
		}
	}

	//包含 删除叶节点,删除中间节点 2种情况
	if(temp->size==1)
	{
		//删除叶节点,需要检查树是否仅1个节点
		if(!scan_nodes.empty())
		{
			sbt_node *parent=scan_nodes.top();
			if(parent->left==temp)
			{
				parent->left=NULL;
			}
			else
			{
				parent->right=NULL;
			}
		}
		free(temp);
	}
	else
	{
		//删除中间节点时,按照左右子树节点的数目,依据平衡原则,将数目多的子树中
		//距删除节点最近的节点取代被删除节点,与avl处理相同
		//此时沿途节点size-1,同时需要对沿途节点逐级调用平衡过程.
		temp->size--; //此节点size-1

		//开始找寻最近的节点
		sbt_node *near_node;
		if((temp->right==NULL) || ((temp->left!=NULL) && (temp->left->size > temp->right->size)))
		{
			//左侧子树较重,选择前一个节点
			//同时搜索沿途节点size-1
			near_node=temp->left;
			stack<sbt_node *> scan_near_nodes;
			while (near_node->right!=NULL)
			{
				near_node->size--;
				scan_near_nodes.push(near_node);
				near_node=near_node->right;
			}
			//此时near_node为需要的节点,
			//此节点或为叶节点,或仅有1个左节点
			temp->key=near_node->key;
			if(near_node->left==NULL)
			{
				//此时near_node是叶节点
				//此时可能scan_near_nodes未包含内容,即temp为near_node的父节点
				if(!scan_near_nodes.empty())
				{
					sbt_node *parent=scan_near_nodes.top();
					parent->right=NULL;
				}
				else
				{
					//此时temp为near_node的父节点,直接将temp左分支设置为NULL
					temp->left=NULL;
				}
				free(near_node);
			}
			else
			{
				//此时near_node有1个左子节点
				near_node->key=near_node->left->key;
				near_node->size--;
				free(near_node->left);
				near_node->left=NULL;
			}
			//对于找寻near_node过程中经过的节点逐级平衡
			while(!scan_near_nodes.empty())
			{
				maintain(scan_near_nodes.top(),false);
				maintain(scan_near_nodes.top(),true);
				scan_near_nodes.pop();
			}
		}
		else
		{
			//右侧子树较重,选择后一个节点
			//同时搜索沿途节点size-1
			near_node=temp->right;
			stack<sbt_node *> scan_near_nodes;
			while (near_node->left!=NULL)
			{
				near_node->size--;
				scan_near_nodes.push(near_node);
				near_node=near_node->left;
			}
			//此时near_node为需要的节点,
			//此节点或为叶节点,或仅有1个右节点
			temp->key=near_node->key;
			if(near_node->right==NULL)
			{
				//此时near_node是叶节点
				//此时可能scan_near_nodes未包含内容,即temp为near_node的父节点
				if(!scan_near_nodes.empty())
				{
					sbt_node *parent=scan_near_nodes.top();
					parent->left=NULL;
				}
				else
				{
					//此时temp是near_node的父节点,直接将temp右分支设置为NULL
					temp->right=NULL;
				}
				free(near_node);
			}
			else
			{
				//此时near_node有1个右子节点
				near_node->key=near_node->right->key;
				near_node->size--;
				free(near_node->right);
				near_node->right=NULL;
			}
			//对于找寻near_node过程中经过的节点逐级平衡
			while(!scan_near_nodes.empty())
			{
				maintain(scan_near_nodes.top(),false);
				maintain(scan_near_nodes.top(),true);
				scan_near_nodes.pop();
			}
		}
	}

	//按照删除过程中的节点来平衡
	while(!scan_nodes.empty())
	{
		maintain(scan_nodes.top(),false);   //对于沿途节点执行平衡过程
		maintain(scan_nodes.top(),true);    //对于沿途节点执行平衡过程
		scan_nodes.pop();
	}
	return 0;
}

//维护部分
//实际是使用avl标准的处理方法,LL,LR,RL,RR 等4钟处理情况
//flag为true表示检查右侧子节点是否不小于左侧孙子节点,否则检查左侧子节点是否不小于右侧孙子节点
void maintain(sbt_node *node,bool flag)
{
//    if(!sbt_validate(node))
//    	cout<<"before maintain ,the validate is failed"<<endl;

    if(flag)
	{
		//检查右侧部分是否符合要求
		if((node->left!=NULL ) && (node->left->left!=NULL) &&
				((node->right==NULL) || (node->left->left->size > node->right->size))
		)
		{
			//LL型
			//执行顺时针右旋
			right_rotation(node);
			maintain(node->right,false); //检查右子树的左侧部分
			maintain(node,false);        //检查左侧部分
		}
		else if((node->left!=NULL) && (node->left->right!=NULL) &&
				((node->right==NULL) || (node->left->right->size > node->right->size))
		)
		{
			//LR型
			//先执行逆时针左旋,随后顺时针右旋
			left_rotation(node->left);
			right_rotation(node);
			maintain(node->left,true);   //检查左子树的右侧
			maintain(node->right,false); //检查右子树的左侧
			maintain(node,false);        //分别检查左侧和右侧
			maintain(node,true);         //分别检查左侧和右侧
		}

	}
	else
	{
		//检查左侧部分是否符合要求
		if((node->right!=NULL) && (node->right->left!=NULL) &&
				((node->left==NULL) || (node->left->size < node->right->left->size))
		)
		{
			//RL型
			//先执行顺时针右旋,随后执行逆时针左旋
			right_rotation(node->right);
			left_rotation(node);
			maintain(node->left,true);    //检查左子树的右侧
			maintain(node->right,false);  //检查右子树的左侧
			maintain(node,false);        //分别检查左侧和右侧
			maintain(node,true);         //分别检查左侧和右侧
		}
		else if((node->right!=NULL) && (node->right->right!=NULL) &&
				((node->left==NULL) || (node->left->size < node->right->right->size))
		)
		{
			//RR型
			//执行逆时针左旋
			left_rotation(node);
			maintain(node->left,true);    //检查左子树的右侧
			maintain(node,true);          //检查右侧
		}
	}

//    if(!sbt_validate(node))
//    	cout<<"after maintain ,the validate is failed"<<endl;
}

bool sbt_validate(sbt_node *node)
{
	if(node==NULL)
		return true;
	if(node->left==NULL && node->right==NULL )
	{
		if(node->size==1)
			return true;
		else
			return false;
	}

	if(node->left!=NULL && node->left->key >node->key)
	{
		cout<<"node->left->key:"<<node->left->key<<",node->key:"<<node->key<<endl;
		return false;
	}
	if(node->right!=NULL && node->right->key < node->key)
	{
		cout<<"node->right->key:"<<node->right->key<<",node->key:"<<node->key<<endl;
		return false;
	}

	int l,r;
	l= (node->left!=NULL)? node->left->size:0;
	r= (node->right!=NULL)? node->right->size:0;
	if(node->size != l+r+1)
	{
		cout<<"node->size:"<<node->size<<",l:"<<l<<",r:"<<r<<",node->key"<<node->key<<endl;
		return false;
	}

	int ll,lr,rl,rr;
	if(node->left!=NULL)
	{
		ll=(node->left->left!=NULL)? node->left->left->size:0;
		lr=(node->left->right!=NULL)? node->left->right->size:0;
		if(r<ll ||r<lr)
		{
		    cout<<"r:"<<r<<",ll:"<<ll<<",lr:"<<lr<<",node->key"<<node->key<<endl;
			return false;
		}
	}
	if(node->right!=NULL)
	{
		rl=(node->right->left!=NULL)? node->right->left->size:0;
		rr=(node->right->right!=NULL)? node->right->right->size:0;
		if(l<rl || l<rr)
		{
		    cout<<"l:"<<l<<",rl:"<<rl<<",rr:"<<rr<<",node->key"<<node->key<<endl;
			return false;
		}
	}

	return sbt_validate(node->left) && sbt_validate(node->right);

}


 

使用递归版本:

//使用递归来实现insert和delete
#include <stdlib.h>

#include "sbt.h"

//插入过程中,先搜索到合适的位置,插入节点,
//随后逐级向上平衡
//使用递归来解决
//成功插入返回0, 无法分配内存返回-1,存在重复值返回-2
int sbt1_insert(sbt_node **node,int key)
{
	int i;
	if((*node)==NULL)
	{
		//当前为起始节点
		(*node)=(sbt_node *)malloc(sizeof(sbt_node));
		if((*node)==NULL)
			return -1;  //分配内存失败,返回-1
		(*node)->left=NULL;
		(*node)->right=NULL;
		(*node)->key=key;
		(*node)->size=1;
		return 0;
	}

	if((*node)->key > key)
	{
		//在左侧插入
		i=sbt1_insert(&((*node)->left),key);
		//由于左侧插入1个节点,所以需要检查右侧分支是否符合SBT要求
		//左侧分支则一定符合要求
		if(i==0)
		{
			(*node)->size++;
			maintain((*node),true);
		}
	}
	else if((*node)->key < key)
	{
		//在右侧插入
		i=sbt1_insert(&((*node)->right),key);
		//由于右侧插入1个节点,所以需要检查左侧分支是否符合SBT要求
		//右侧分支则一定符合要求
		if(i==0)
		{
			(*node)->size++;
			maintain((*node),false);
		}
	}
	else
	{
		//存在重复,插入失败
		return -2;
	}

	return i;
}

//删除,先搜索到对应的节点,
//逐级向上平衡
//成功返回0,树中不存在对应key的节点,则返回-1
//使用递归来解决
int sbt1_delete(sbt_node **node,int key)
{
	int i;
	if((*node)==NULL)
	{
		//没有对应的key
		return -1;
	}

	if((*node)->key > key)
	{
		i=sbt1_delete(&((*node)->left),key);
		//由于左分支删除1个节点,所以需要检查左侧分支是否符合SBT要求
		if(i==0)
		{
			(*node)->size--;
			maintain((*node),false);
		}
		return i;
	}
	if((*node)->key < key)
	{
		i=sbt1_delete(&((*node)->right),key);
		//由于右分支删除1个节点,所以需要检查右分支是否符合SBT要求
		if(i==0)
		{
			(*node)->size--;
			maintain((*node),true);
		}
		return i;
	}

	//此时找到需要删除的节点,判断是否存在左右分支
	sbt1_delete_current(node);
	return 0;
}

//将当前节点删除,平衡左右分支
void sbt1_delete_current(sbt_node **node)
{
	if((*node)->size > 2)
	{
		//此时左右分支均存在,按照分支重量来决定
		//将最多子树中最近节点的值代替删除的值
		(*node)->size--;
		if((*node)->left->size > (*node)->right->size)
		{
			//左分支较重,删除左分支中最大节点,同时返回此节点的值
			(*node)->key=sbt1_delete_max(&((*node)->left));
			maintain((*node),false);
		}
		else
		{
			//右分支较重,删除右分支中最小节点,同时返回此节点的值
			(*node)->key=sbt1_delete_min(&((*node)->right));
			maintain((*node),true);
		}
		return;
	}
	if((*node)->size ==2)
	{
		//存在1个子节点
		(*node)->size --;
		sbt_node **child;
		child=((*node)->left!=NULL) ? (&((*node)->left)):(&((*node)->right));
		(*node)->key=(*child)->key;
		free((*child));
		(*child)=NULL;
		return;
	}

	//节点为叶节点
	free((*node));
	(*node)=NULL;
	return;
}
//删除此树中最大节点,同时返回此节点的值
int sbt1_delete_max(sbt_node **node)
{
	int i;
	if((*node)->size==1)
	{
		//仅存在1个节点
		i=(*node)->key;
		free((*node));
		(*node)=NULL;
		return i;
	}
	if((*node)->size==2 && (*node)->left!=NULL)
	{
		//仅存在1个左子节点
		i=(*node)->key;
		(*node)->size--;
		(*node)->key=(*node)->left->key;
		free((*node)->left);
		(*node)->left=NULL;
		return i;
	}
	(*node)->size--;                        //节点总数-1
	i=sbt1_delete_max(&((*node)->right));   //递归调用
	maintain((*node),true);                 //由于删除的节点总是在右分支,所以需要检查右分支是否符合SBT要求
	return i;
}

//删除此树中最小节点,同时返回此节点的值
int sbt1_delete_min(sbt_node **node)
{
	int i;
	if((*node)->size==1)
	{
		//仅存在1个节点
		i=(*node)->key;
		free((*node));
		(*node)=NULL;
		return i;
	}
	if((*node)->size==2 && (*node)->right!=NULL)
	{
		//仅存在1个左子节点
		i=(*node)->key;
		(*node)->size--;
		(*node)->key=(*node)->right->key;
		free((*node)->right);
		(*node)->right=NULL;
		return i;
	}
	(*node)->size--;                        //节点总数-1
	i=sbt1_delete_min(&((*node)->left));    //递归调用
	maintain((*node),false);                //由于删除的节点总是在左分支,所以需要检查左分支是否符合SBT要求
	return i;
}

//按照从小到大排列,开始位置为1,删除第rank_level的节点,
//如果rank_level大于总节点数,则删除最大节点,若rank_level小于1,则删除最小节点
//同时返回此节点值
int sbt1_delete_rank(sbt_node **node,int rank_level)
{
	int i;

	if((*node)->size==1)
	{
		//仅存在1个节点
		i=(*node)->key;
		free((*node));
		(*node)=NULL;
		return i;
	}

	if(((*node)->left!=NULL) && ((*node)->left->size >=rank_level))
	{
		//rank_level节点在左侧
		(*node)->size--;                                   //节点总数-1
		i=sbt1_delete_rank(&((*node)->left),rank_level);   //递归调用
		maintain((*node),false);                           //由于删除的节点在左分支,所以需要检查左分支是否符合SBT要求
	}
	else if((((*node)->left!=NULL) && ((*node)->left->size +1 < rank_level)) ||
			(((*node)->left==NULL) && rank_level>=2))
	{
		//rank_level节点在右侧
		(*node)->size--;                                   //节点总数-1
		if((*node)->left!=NULL)
		{
			rank_level=rank_level - (*node)->left->size - 1;
		}
		else
		{
			rank_level--;
		}
		i=sbt1_delete_rank(&((*node)->right),rank_level);  //递归调用
		maintain((*node),true);                            //由于删除的节点在右分支,所以需要检查右分支是否符合SBT要求
	}
	else
	{
		//此时节点为需要删除的节点
		i=(*node)->key;
		sbt1_delete_current(node);
	}
	return i;
}


 

 

#include <stddef.h>

#define MAX_HEIGHT 32    //定义最大高度,使用stack模板后,暂时未使用

/* sbt数据结构 */
struct sbt_node
{
  struct sbt_node *left,*right;  //左右子树
  int key;                       //按照这个值来比较
  int size;                   //定义节点数目
};

void left_rotation(sbt_node *node);
void right_rotation(sbt_node *node);
sbt_node *sbt_find(sbt_node *node,int key);
sbt_node *sbt_rank(sbt_node *node,int rank_level);
int order_position(sbt_node *node,int key);

sbt_node *sbt_prev(sbt_node *root,sbt_node *node);
sbt_node *sbt_next(sbt_node *root,sbt_node *node);
sbt_node *sbt_min(sbt_node *node);
sbt_node *sbt_max(sbt_node *node);

sbt_node *sbt_insert(sbt_node *node,int key);
int sbt_delete(sbt_node *node,int key);

//使用递归来插入删除
int sbt1_insert(sbt_node **node,int key);
int sbt1_delete(sbt_node **node,int key);
int sbt1_delete_max(sbt_node **node);
int sbt1_delete_min(sbt_node **node);
int sbt1_delete_rank(sbt_node **node,int rank_level);
void sbt1_delete_current(sbt_node **node);

void maintain(sbt_node *node,bool flag);

bool sbt_validate(sbt_node *node);


 

测试主程序很简单:

//使用sbt树来实现leon提出的问题
#include <iostream>
#include <stdlib.h>
#include <time.h>

#include "sbt.h"

using namespace std;

int main()
{
	sbt_node *root=sbt_insert(NULL,0);
	for(int i=1;i<1000000;i++)
	{
		sbt1_insert(&root,i);
	}
//	if(!sbt_validate(root))
//	{
//		cout<<"after insert, the validate is failed"<<endl;
//		return -1;
//	}
//	else
//		cout<<"after insert, the validate is OK"<<endl;


	srand(time(NULL));

	for (int i=0;i<1000000;i++)
	{
		int j=rand()%root->size;

//		sbt_node *node=sbt_rank(root,j+1);
//		int key=node->key;
//		cout<<key<<endl;
//		sbt1_delete(&root,key);

		cout<<sbt1_delete_rank(&root,j+1)<<endl;

//		if(!sbt_validate(root))
//		{
//			cout<<"after delete, the validate is failed"<<endl;
//			cout<<"current i "<<i<<endl;
//			abort();
//		}
	}

//	int del[3]={269795,269799,269800};
//	for(int i=0;i<3;i++)
//	{
//		sbt_delete(root,del[i]);
//		if(!sbt_validate(root))
//		{
//			cout<<"after delete, the validate is failed"<<endl;
//			abort();
//		}
//	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值