树形分区设计采用C++实现排序

当n比较大时,需要更新大量元素,且元素的值变化比较快的情况下使用:

我们可以把[0, 1,000,000)作为一级区间;再把一级区间分为两个2级区间[0, 500,000), [500,000, 1,000,000),然后把二级区间二分为4个3级区间[0, 250,000), [250,000, 500,000), [500,000, 750,000), [750,000, 1,000,000),依此类推,最终我们会得到1,000,000个21级区间[0,1), [1,2) … [999,999, 1,000,000)。这实际上是把区间组织成了一种平衡二叉树结构,根结点代表一级区间,每个非叶子结点有两个子结点,左子结点代表低分区间,右子结点代表高分区间。树形分区结构需要在更新时保持一种不变量(Invariant):非叶子结点的count值总是等于其左右子结点的count值之和。

range_tree

以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的log(n)级别的,也就是说如果用户积分一次变化在百万级,更新区间的数量在二十这个级别。在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499,000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500,000),那么该用户排名应该在右子树[500,000, 1,000,000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250,000, 500,000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的…;直到最后我们把用户积分精确定位在21级区间[499,000, 499,001),整个累加过程完成,得出排名!

虽然,本算法的更新和查询都涉及到若干个操作,但如果我们为区间的from_score和to_score建立索引,这些操作都是基于键的查询和更新,不会产生表扫描,因此效率更高。另外,本算法并不依赖于关系数据模型和SQL运算,可以轻易地改造为NoSQL等其他存储方式,而基于键的操作也很容易引入缓存机制进一步优化性能。进一步,我们可以估算一下树形区间的数目大约为2,000,000,考虑每个结点的大小,整个结构只占用几十M空间。所以,我们完全可以在内存建立区间树结构,并通过user_score表在O(n)的时间内初始化区间树,然后排名的查询和更新操作都可以在内存进行。一般来讲,同样的算法,从数据库到内存算法的性能提升常常可以达到10^5以上;因此,本算法可以达到非常高的性能。

 

#include <iostream>
class BinaryTreeClass
{
public:
 int count;
 int from_score;
 int to_score;
 static int indexout;
 int index;
 BinaryTreeClass* left;
 BinaryTreeClass* right;
 BinaryTreeClass(int from, int to,int c = 0):from_score(from), to_score(to), count(c), left(NULL), right(NULL)
 {
  index = indexout;
  indexout ++;
 }
};
int BinaryTreeClass::indexout = 0;

BinaryTreeClass* createTreeClass(BinaryTreeClass* root, int from, int to)
{
 if(from > to) 
 {
  return NULL; //new 18 times, 18 nodes
 }
 root = new BinaryTreeClass(from, to);
 if(from == to) 
  return root;
 int mid = from + ((to-from)>>1);
  root->left = createTreeClass(root->left, from, mid);
  root->right = createTreeClass(root->right, mid+1, to);
 return root;
}
void insertNewNode(BinaryTreeClass* root, int score)
{
 if(root == NULL)
 {
  return;
 }
 if(score>= root->from_score && score<=root->to_score)
 {
  root->count++;
 }
 int mid = root->from_score + ((root->to_score - root->from_score)>>1);
 if(score <= mid)
 {
  insertNewNode(root->left,score);
 }
 else
 {
  insertNewNode(root->right, score);
 }
}
void getRank1(BinaryTreeClass* root, int score, int& rank)
{
 //int rank = 1; 迭代不能有变量,不然每次都会归为1
 if(root == NULL) return;
 int mid = root->from_score + ((root->to_score - root->from_score)>>1);
 if(score <= mid )
 {
  if(root->right != NULL)
  {
  rank += root->right->count;
  }
  getRank1(root->left, score, rank);
 }
 else
 {
  getRank1(root->right, score, rank);
 }
 

}
int getRank1(BinaryTreeClass* root, int score)
{
 int rank = 1;
 getRank1(root, score, rank); // 于是采用两层包裹
 return rank;
}

void findNodeByIndex(BinaryTreeClass* root, int index, int& from, int& to)//find node by index from root
{ 
 if(NULL != root)
 {
  findNodeByIndex(root->left, index, from, to);
  if(index == root->index)
  {
   //node = new BinaryTreeNode(root->from_score, root->to_score, root->count,root->index);
   from = root->from_score;
   to = root->to_score;
   return ;
  }
  findNodeByIndex(root->right, index, from, to);
 }
}
void printClass(BinaryTreeClass* root)
{
   if(root != NULL)  
    {  
        printClass(root -> left);  
        cout << "from"<<root -> from_score << "  " << "to"<<root -> to_score << "  " <<"index"<<root->index<<" "<<"cout"<< root -> count << endl;  
        printClass(root -> right);  
    }  
}
  
int main()
{
 BinaryTreeClass* root = NULL;
 BinaryTreeClass* tree = createTreeClass(root, 1, 10);
 printClass(tree); //its null right now
 cout<<"init......."<<endl;
 int score[10] = {1,3,5,6,2,4,3,1,7,7};
 for(int i =0; i < 10; i++) insertNewNode(tree, score[i]);
 printClass(tree);
 cout<<"get rank...."<<endl;
    cout<<"rank of 5"<<getRank1(tree,5)<<endl;;
 //BinaryTreeNode* node = NULL;
 int from = 0, to = 0;
 findNodeByIndex(tree, 5, from, to);
 cout<<"node-from"<<from<<"node1->to"<<to<<"index"<<5<<endl;

  BinaryTree* root1 = NULL;
 createTree(root1, 1, 10);
 print(root1);
 system("pause");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值