我正在浏览算法在线课程,有时他们会提出未经提及的奖励挑战,但未提供答案。这是其中之一:
- 您将获得正整数k。
- 您将从标准输入中读取一系列字符串(总共
n
个字符串;在您用完所有字符串之后,您才知道n
。)- 您可以使用具有基本API的随机队列:
size()
返回队列中的元素数量;enqueue(String)
将字符串添加到队列中;并且dequeue()
从队列中删除并返回一个字符串,随机统一选择。- 从
k
字符串集合中随机选择统一选择的所有输入和结尾打印n
字符串。- 使用不大于
醇>k
的随机队列。
我无法同时满足4
和5
。如果我使用n
字符串填充队列,然后对k
进行dequeue()
次调用,或者,我可以设计输出的分布均匀我在队列中最多只有k
个元素的方案,但输出不均匀,因为在开头读取的字符串最后有一个或多或少的机会成为最终的一部分选择集(取决于我选择的算法)。
如果我事先知道n
,我可以为我读取的每个字符串分配0和n
之间的随机ID,并保留k
个最小ID及其各自字符串的列表(例如k_smallest
);如果为新字符串分配的随机ID小于我已有的k
中的任何一个,我可以决定从k_smallest
中删除最大的元素并将新字符串添加到其中。但是,出现了两个问题:在读取所有字符串之后才知道n
,并且随机化队列不允许将最大元素出列,只有一个随机出列。
我对解决方案非常好奇。如何使用与k
而非n
成比例的空间来解决这个问题?
答案 0 :(得分:1)
关键:
您需要跟踪目前已阅读的元素数量。
ALGO:
l:到目前为止enqueue(..)
次呼叫。
取forst k元素并将它们放入大小为k的内部存储空间中。 (例如,大小为k的阵列)。设置l:=k
对于第一个k之后的每个enqueue(..)
调用,您需要决定要删除哪个元素。如果你已经将l个元素排队,那么我们需要保留新元素的概率是k / l。如果随机生成器说保留它,则必须删除旧元素的随机元素。并用新的替换它。 l:=l+1
您可以随时在内部存储空间中均匀分布目前已排队的值(l
)。最后是l==n
。
P.S。
对于k = 1,该算法更直观。因此,如果您在获得想法时遇到问题,请考虑最简单的情况k = 1。