这篇文章是来源于去年同事询问的一个问题. 同事需要将数据库中的一些敏感信息实现遮蔽. 由于客户要求的规则比较复杂, 所以无法使用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;
}