题目描述
描述:假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:
实现 track(int x) 方法,每读入一个数字都会调用该方法;
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
注意:本题相对原题稍作改动
示例:
输入:
["StreamRank", "getRankOfNumber", "track", "getRankOfNumber"]
[[], [1], [0], [0]]
输出:
[null,0,null,1]
提示:
x <= 50000
track 和 getRankOfNumber 方法的调用次数均不超过 2000 次
解题思路
思路1:最直观的想法是,插入排序+二分查找。由于每读入一个数字就会调用track方法,故可以使用插入排序,每次插入一个元素时,可以认为原有的元素已经有序,故从后向前遍历数组,如果当前元素小于前一个元素,则交换两个元素,比如1 2 3 4,插入元素2,那么从后向前遍历1 2 3 4 2,第一次交换得到1 2 3 2 4,第二次交换得到1 2 2 3 4。由于要求求解小于等于x的值的个数,故应该找到第一个大于x的值所在的位置,那么此时我们可以使用左开右闭区间[left,right),当res[mid]小于等于x,就令left=mid+1,反之当res[mid]大于x,则令right=mid,因为此时mid可能是第一个大于x的喔!
vector<int> res;
StreamRank() {
}
void track(int x)
{
//每读入一个数字就会调用该方法 故可以使用插入排序
res.push_back(x);
//插入排序从后向前
for(int i=res.size()-1;i>0;i--)
{
if(res[i]<res[i-1])
swap(res[i],res[i-1]);
}
}
int getRankOfNumber(int x)
{
//有序则使用二分查找找上界
int left=0,right=res.size();
//左开右闭区间!
while(left<right)
{
int mid=left+(right-left)/2;
//找到第一个大于x的数
if(res[mid]<=x)
left=mid+1;
//大于则有可能是第一个大于x的
else
right=mid;
}
return left; //left指向的是第一个大于x的下标 那么left+1-1就是个数
}
总结:要找的是第一个大于x的值!故使用[left,x+1),很自然而然的使用左开右闭区间!如果使用的是[left,x],那么由于存在重复元素,故很难界定是否找到的是最后一个x。