Rank from Stream:设计一个在线算法,包含track()
和getRankOfNumber()
两个方法。track(int x)
读取输入中的整数并存储,getRankOfNumber(int x)
返回已经读取的不大于x
的整数数量。
最先想到的方法就是把读取的数据按序存入数组中:
track()
的逻辑就是找到第1
个比x
大的数,然后将这个数以及后面的数向后移动,并将x
插入getRankOfNumber()
的逻辑就是找到第1
个比x
大的数,然后返回x
的下标就可以了(注意书上要求当x
数存在的多个时,返回的结果不应该包含x
本身的计数,也就是返回最后一个x
的下标)
class StreamRank {
private:
vector<int> vec;
size_t binarySearch(const int x)
{
size_t begin = 0, end = vec.size();
while(begin < end){
size_t mid = begin + (end - begin) / 2;
if(vec[mid] <= x) begin = mid + 1;
else end = mid;
}
return begin;
}
public:
StreamRank() {
}
void track(int x) {
size_t pos = binarySearch(x);
vec.push_back(0);
for(size_t i = vec.size(); i > pos && i > 1; i--)
{
vec[i - 1] = vec[i - 2];
}
vec[pos] = x;
}
int getRankOfNumber(int x) {
return binarySearch(x);
}
};
/**
* Your StreamRank object will be instantiated and called as such:
* StreamRank* obj = new StreamRank();
* obj->track(x);
* int param_2 = obj->getRankOfNumber(x);
*/
由于上一题的下标,我一度以为二分法查找第一个大于目标值的数都不会写了。采用顺序存储时,两种算法的复杂度都可以达到O(logn)
,但是track()
需要批量移动元素。
为了避免这一点,可以选择采用二叉搜索树的形式,其中每个节点包含子节点指针,左子树大小leftSubSize
和重复次数dup
,这样在查找的过程中,累加leftSubSize
和dup
即可。
class StreamRank {
private:
struct Node
{
int value, leftSubSize, dup;
Node* left;
Node*right;
Node(int value) : value(value), leftSubSize(0), dup(1), left(nullptr), right(nullptr){};
};
Node* root;
public:
StreamRank() {
root = nullptr;
}
void track(int x) {
Node** curr = &root;
while(*curr != nullptr){
if(x < (*curr)->value){
(*curr)->leftSubSize++;
curr = &(*curr)->left;
}
else if(x > (*curr)->value){
curr = &(*curr)->right;
}
else{
(*curr)->dup++;
return;
}
}
*curr = new Node(x);
return;
}
int getRankOfNumber(int x) {
Node** curr = &root;
int ret = 0;
while(*curr != nullptr){
if(x < (*curr)->value){
curr = &(*curr)->left;
}
else if(x > (*curr)->value){
ret += (*curr)->leftSubSize + (*curr)->dup;
curr = &(*curr)->right;
}
else{
ret += (*curr)->leftSubSize + (*curr)->dup;
return ret;
}
}
return ret;
}
};
/**
* Your StreamRank object will be instantiated and called as such:
* StreamRank* obj = new StreamRank();
* obj->track(x);
* int param_2 = obj->getRankOfNumber(x);
*/