algorithm - 编写一个程序从一个十亿元素的数组中寻找100个最大编号

  显示原文与译文双语对照的内容
0 0

我最近出席接受记者采访时,有人问我在"写一个程序来查找最大 100 1亿数字的数组的数据都过期了。"

我当时只能够给一个暴力破解溶液,加在最后一个 100 O(nlogn) 时间复杂度,并采取了对该数组进行排序是页码了。

 
Arrays.sort(array);

 

面试官一直在寻找一个更好的时间复杂度,我给你一对其他的解决方案但是没回答他。 是否有更好的时间复杂度解决方案?

时间: 原作者:

0 0

你可以保持 100个最大数字的优先级队列,当遇到一个大于队列( 队列的头部) 中的最小值的数字时,迭代 billion,删除队列的头部并将新号码添加到队列中。

代价为的编辑: 为 Dev,使用堆实现的优先级队列,队列的复杂性是 O(logN)

在最糟糕的情况下,你得到的billion*log2(100)billion*log2(billion)

一般情况下,如果你需要一组N 个数字的最大K 值,复杂度是 O(NlogK) 而不是 O(NlogN),这在K 非常小的情况下非常重要。

EDIT2:

这里算法的预期时间非常有趣,因为在每次迭代中,插入可能会发生,也可能不会发生。 '要插入队列的数字是一个随机变量的概率,它大于来自同一个分布的i-K 随机变量( 第一个k 数字自动添加到队列中) )的概率。 我们可以使用顺序统计( 参见链接 ) 来计算这个概率。 例如让我们假定所有的号码都从 {0, 1} 随机选择的一致,这个预期值的( i-K ) 思索( 超出我的数字) 是 (i-k)/i 数和的机会要大于此值 1-[(i-k)/i] = k/i 一个随机变量。

因此,预期的插入数量为:

enter image description here

所期望的运行时间可以表示为:

enter image description here

( k 时间生成带有第一个 k 元素的队列,然后是 n-k 比较,以及如上所述的插入数量,每一个都需要平均 log(k)/2 时间)

注意,当 Nk 比较大时,这个表达式更接近 N 而不是 NlogK 。 这有点直观,就像在问题的情况下,即使在 10000迭代( 与十亿的比较是非常小的) 之后,将一个数字插入队列的几率也很小。

原作者:
0 0

如果在面试中询问,我认为面试官可能希望看到你的问题解决过程,而不仅仅是你对算法的知识。

描述很笼统,所以你可以问他这些数字的范围或者意义,使问题变得清晰。 这样做可能会给面试官留下印象。 例如如果这些数字代表一个国家( 中国)的人的年龄,那么这个问题就更容易了。 通过合理的假设,没有人比 200岁,你可以使用一个大小为 200 ( 可能 201 )的int数组来计算在一个迭代中具有相同年龄的人的数量。 这里的索引表示年龄。 在这之后,找到 100个最大的数字是小菜一碟。

总之,让问题更加具体和清晰对你在面试中是有利的。

原作者:
0 0

你可以迭代使用 O(n )的数字

每当发现大于当前最小值的值时,将新值添加到具有大小为 100的循环队列。

循环队列的最小值是新的比较值。 继续添加到那个队列。 如果已满,从队列中提取最小值。

原作者:
0 0

我意识到这是用'算法'标记的,但是会抛出一些其他的选项,因为它也应该被标记为'面试'。

1亿数字的来源是什么? 如果是一个数据库,那么'从表 ORDER BY 值desc限制 100 select中选择值'会做得很好- 可能有方言差异。

这是一次性的还是要重复的? 如果重复,多少频率如果是一次性的,数据在一个文件中,那么'cat srcfile | 排序( 需要的选项) ) | -100会让你在计算机处理这个琐碎的琐事时快速做一些卓有成效的工作。

如果重复的话,你将建议选择任何适当的方法来获得初始答案并存储/缓存结果,这样你就可以持续报告最上面的100.

最后,考虑到了。 你是在寻找一个入门级别的工作,并与一个令人讨厌的经理或者未来的同事面谈? 如果是这样,那么你可以扔掉各种方法来描述相关的技术利弊。 在压缩感知trivia,如果你正在寻找一个多个管理职务,那么方法它好像一个管理者那样关心解决方案的开发和维护成本,并假设"非常感谢"留言,如果那是面试官希望中心元素展开 他和你在那里不太可能有太多的进步潜力。

下次面试时更走运。

原作者:
0 0

你可以使用快速选择算法在 the(by order) 索引 [billion-101] 中查找数字,然后在数字上迭代,并查找从该数字更大的数字。


array={...the billion numbers...} 
result[100];

pivot=QuickSelect(array,billion-101);//O(N)

for(i=0;i<billion;i++)//O(N)
 if(array[i]>=pivot)
 result.add(array[i]);

这里算法的时间为: 2 X O(N) = O(N) ( 平均案例性能)

第二个选项如 Jungblut 建议是:

在该使用建最大堆将采取 O(N), 又 100顶部最大数字将会在堆的顶部,所有你需要的是,让他们出去。

该算法时间则为o ( N ) + 100 X O(log(n)) = O(N)

原作者:
0 0

任何一个time,手头上我的即时React来寻找它,就是使用一个堆,但有方法采用的无保持所提交的所有QuickSelect而

创建大小为 200的数组,并用前 200个输入值填充。 运行QuickSelect并丢弃低 100,剩下 100个空闲位置。 在接下来的100个输入值中读取并再次运行 QuickSelect 。 继续,直到你以 100的批处理运行整个输入。

最后你有了前 100个值。 对于N 个值,你运行QuickSelect大约是N/100次。 各有关 200 Quickselect成本乘以一些稳定,因此总体成本是 2 N 乘以一些常数。 对我来说这看起来线性,输入的大小中的说明我是连接大脑要 100在本说明的,而不管参数的大小

0 0

尽管另一个quickselect解决方案已经被downvoted化,但事实仍然是quickselect会比使用一个大小为 100的队列更快地找到解决方案。 Quickselect在比较方面有预期运行时间 2 n + o(n), 。 一个非常简单的实现是


array = input array of length n
r = Quickselect(array,n-100)
result = array of length 100
for(i = 1 to n)
 if(array[i]>r)
 add array[i] to result

这将对平均进行 3个n + o(n) 比较。 此外,它可以让它更高效使用这一事实quickselect将数组中的最大的100项留在 100 right-most地点。 实际上,运行时间可以提高到 2 n+o ( n ) 。

这是预期的运行时间,而不是最坏的情况,而是使用一个体面的pivot 选择策略( 例如。 于一个任意小的常数c,相关的那些作为 pivot 21随机选出 21元素,然后选择中间值),那么比较可能的减少被以高概率确认处在大多数( 2 +c )的数量n

实际上,使用优化的抽样策略( 例如。 样例 sqrt(n) 元素随机,并选择 99个百分点,运行时间可以归结为( 1 +c ) n + o(n),对于任意小的c ( 假设要选择的元素数是 o(n)) ) 。

另一方面,使用大小为 100的队列将需要 O(log(100)n) 比较,并且 2的日志基数 100大约等于 6.6.

如果我们认为这个问题的的更抽象的意义上说中选择最大的K 元素从一个数组,它的大小N,其中 K=o ( N ) 却同时K 和N 趋于无穷大,则运行的时间将被 O(N) quickselect版本和队列版本将在 O(N log K), 所以从这个意义上说quickselect也渐近优越。

在注释中提到,队列解决方案将在随机输入的期望时间N + K 日志中运行。 当然,随机输入假设从不有效,除非问题显式地声明。 队列解决方案可以随机顺序遍历数组,但这将导致额外的成本,N 调用一个随机数生成器,或者是permuting的整个输入数组,或者分配一个包含随机索引的新数组。

如果问题不允许你移动原始数组中的元素,并且分配内存的开销很高,那么复制数组不是一个选项,这就是另一个问题。 但是在运行时间方面,这是最好的解决方案。

原作者:
0 0

获取前 100亿的数字并排序。 现在在中环order,正确遍历亿,如果源 100,insert.编号比最小的。 你最终得到的是比 O(n) 更接近于集合大小的东西。

0 0

两个选项:

( 1 ) 堆( priorityQueue )

保持min-heap大小为 100. 遍历数组。当元素小于堆中的第一个元素时,替换它。


InSERT ELEMENT INTO HEAP: O(log100)
compare the first element: O(1)
There are n elements in the array, so the total would be O(nlog100), which is O(n)

( 2 ) Map-reduce模型。

这与hadoop中的字数统计非常相似。 映射作业:计算元素或者时间的每一个频率。 减少:获取顶部K 元素。

通常我会给招聘者两个答案。 给他们任何他们喜欢的东西。 当然,映射减少编码是 labor-some,因为你必须知道每个精确的参数。 没有任何害处。 好运。

原作者:
0 0

一个非常简单的解决方案是迭代数组 100次。 哪个是 O(n)

每次取出最大的数字( 并将它的值更改为最小值,这样你就不会在下一次迭代中看到它,或者跟踪以前的答案的索引( 通过跟踪原始数组可以有多个相同的数字) ) 。 100迭代后,你有 100个最大的数字。

原作者:
...