这是一个常见的面试问题。 你有一串数字进来(比方说超过一百万)。数字介于[0-999]之间。
Implement a class which supports three methods in O(1)
* insert(int i);
* getMean();
* getMedian();
这是我的代码。
public class FindAverage {
private int[] store;
private long size;
private long total;
private int highestIndex;
private int lowestIndex;
public FindAverage() {
store = new int[1000];
size = 0;
total = 0;
highestIndex = Integer.MIN_VALUE;
lowestIndex = Integer.MAX_VALUE;
}
public void insert(int item) throws OutOfRangeException {
if(item < 0 || item > 999){
throw new OutOfRangeException();
}
store[item] ++;
size ++;
total += item;
highestIndex = Integer.max(highestIndex, item);
lowestIndex = Integer.min(lowestIndex, item);
}
public float getMean(){
return (float)total/size;
}
public float getMedian(){
}
}
我似乎无法想到在O(1)时间内获得中位数的方法。 任何帮助表示赞赏。
答案 0 :(得分:10)
通过构建store
计数器,您已经完成了所有繁重的工作。加上size
值,这很容易。
您只需开始迭代store
,总结计数,直至达到size
的一半。如果size
是奇数,那就是你的中值。对于偶数size
,您将获取周围的两个值并得到它们的平均值。
性能平均为 O(1000/2),这意味着 O(1),因为它不依赖于n
,即性能即使n
达到数十亿,也不会改变。
请记住, O(1)并不意味着即时甚至快速。正如Wikipedia所说:
如果T(n)的值受一个不依赖于该值的值限制,则算法被认为是恒定时间(也写为 O(1)时间)输入的大小。
在你的情况下,该界限是1000。
答案 1 :(得分:3)
您可以阅读的可能值非常有限 - 只有1000.因此您可以考虑实现类似counting sort的内容 - 每次输入数字时,您都会增加该值的计数器。
要在恒定时间内实现中位数,您需要两个数字 - 中位数指数(即中位数值)和您已读取的数值以及中位数左侧(或右侧) 。我将在此停止,希望您能够找到如何继续自己。
EDIT(正如评论中所指出的):您已经拥有了带有排序元素(stored
)的数组,并且您知道中位数左侧的元素数量(size/2
)。您只需要将逻辑粘合在一起。我想指出,如果使用线性附加内存,则不需要在每个插入上遍历整个数组。
答案 2 :(得分:1)
对于一般情况,其中元素范围不受限制,此类数据结构不存在基于任何基于比较的算法,因为它将允许{{1}排序。
证明:假设存在此类DS,请将其设为O(n)
。
让D
为输入数组进行排序。 (假设A
即使是为了简单起见,也可以通过添加垃圾元素并稍后将其丢弃来轻松放松。
A.size()
声明1:此算法在sort(A):
ds = new D()
for each x in A:
ds.add(x)
m1 = min(A) - 1
m2 = max(A) + 1
for (i=0; i < A.size(); i++):
ds.add(m1)
# at this point, ds.median() is smallest element in A
for (i = 0; i < A.size(); i++):
yield ds.median()
# Each two insertions advances median by 1
ds.add(m2)
ds.add(m2)
中运行。
证明:由于我们有add()和median()的常数运算,每次迭代都是O(n)
,迭代次数是线性的 - 复杂性是线性的。
权利要求2:对输出进行排序(A)。
证明(指南):插入n次O(1)
后,中位数是m1
中的最小元素。在将中位数推进一个项目之后的每两次插入,并且由于预先排序,所以对总输出进行排序。
由于上述算法在A
中排序,而在比较模型下不可能,因此不存在此类DS。
QED。