查询 x 所在排位
问题:你正在读一个整数数据流。在读的过程中,你可能需要要知道某个值 x
在已读数据的排位,实现这个功能。也就是说,你需要实现一个方法 track(int x)
,每读入一个整数,就会调用一次该方法。实现方法 getRankOfNumber(int x)
,返回小于等于 x
的值的数量。
简单的方式,维护一个有序的数组。getRankOfNumber
方法很高效,二分查找就好了。但是 track
方法很慢,插入到合适的地方,可能要移动很多元素。
换种数据结构,二分查找树。track
复杂度是
,平均情况,而且我们可以假设数据流是随机的,树大致平衡。getRankOfNumber
方法的效率就不高了,需要中序遍历,得到 x
的排序。
想一想,如果 x
比某个节点的值还小的话,其实就不用去遍历左边的子树了,因为左边的都比 x
小。所以,我们往节点里面添加一个值,记录该节点左边有多少个节点。这样就不用遍历左边的子树,直接加上这个值就可以了。
下面是完整的代码:
private static RankNode root = null;
public static void Track(int number)
{
if (root == null)
{
root = new RankNode(number);
}
else
{
root.Insert(number);
}
}
public static int GetRankOfNumber(int number)
{
return root.GetRank(number);
}
class RankNode
{
public int LeftSize { get; set; }
public RankNode Left { get; set; }
public RankNode Right { get; set; }
public int Data { get; set; }
public RankNode(int d)
{
this.Data = d;
}
public int GetRank(int d)
{
if (d == this.Data)
{
return this.LeftSize;
}
else if (d < this.Data)
{
if (this.Left == null)
{
return -1;
}
else
{
return this.Left.GetRank(d);
}
}
else
{
int rightRank = this.Right == null ? -1 : this.Right.GetRank(d);
if (rightRank == -1)
{
return -1;
}
else
{
return this.LeftSize + 1 + rightRank;
}
}
}
public void Insert(int d)
{
if (d <= this.Data)
{
if (this.Left == null)
{
this.Left = new RankNode(d);
}
else
{
this.Left.Insert(d);
}
this.LeftSize++;
}
else
{
if (this.Right == null)
{
this.Right = new RankNode(d);
}
else
{
this.Right.Insert(d);
}
}
}
}