当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值之和。
以后,每次用户积分有变化所需要更新的区间数量和积分变化量有关系,积分变化越小更新的区间层次越低。总体上,每次所需要更新的区间数量是用户积分变量的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");
}