通过一次遍历获取单向链表中的随机元素

时间:2011-04-29 07:18:00

标签: algorithm random traversal

我有一个方向链表而不知道它的大小。

我想在此列表中获得一个随机元素,我只有一次机会遍历列表。 (我不允许遍历两次或更多次)

这个问题的算法是什么?谢谢!

4 个答案:

答案 0 :(得分:18)

这只是reservoir sampling,水库大小为1。

基本上它非常简单

  1. 选择第一个元素(对于长度为1的列表,第一个元素始终是样本)。
  2. 对于概率为1 / n的每个其他元素,其中n是到目前为止观察到的元素数,您将已经拾取的元素替换为您所在的当前元素。
  3. 这是统一采样的,因为在一天结束时挑选任何元素的概率是1 / n(练习给读者)。

答案 1 :(得分:1)

这可能是一个采访问题。数据科学家使用的数据库采样将大量数据流存储在有限存储空间中。

如果你必须从任何具有元素n的数组中收集k个元素,这样你收集的每个元素的概率应该相同(k / n),你可以按照两个步骤,

1)将第一个k元素存储在存储中。 2)当下一个元素(k + 1)来自流时,显然你的集合中没有空格。生成一个从o到n的随机数,如果生成的随机数小于k假设l,则替换storage [l ]来自流的(k + 1)元素。

现在,回到你的问题,这里的存储大小是1.所以你将选择第一个节点,迭代列表中的第二个元素。现在生成随机数,如果它是1,则单独保留样本,否则切换列表中的存储元素

答案 2 :(得分:0)

这个问题可以使用油藏采样来完成。它基于从n个项目中选择k个随机项目,但这里n可能非常大(它不必适合内存!)和(如你的情况)最初未知。

维基百科有一个可理解的算法,我在下面引用:

array R[k];    // result
integer i, j;

// fill the reservoir array
for each i in 1 to k do
    R[i] := S[i]
done;

// replace elements with gradually decreasing probability
for each i in k+1 to length(S) do
    j := random(1, i);   // important: inclusive range
    if j <= k then
        R[j] := S[i]
    fi
done

这个问题只需要1个值,所以我们采用k = 1。

C实施:

https://ideone.com/txnsas

答案 3 :(得分:0)

这是我找到的最简单的方法,它工作正常并且可以理解:

public int findrandom(Node start) {
    Node curr = start;
    int count = 1, result = 0, probability;
    Random rand = new Random();

    while (curr != null) {
        probability = rand.nextInt(count) + 1;
        if (count == probability)
            result = curr.data;
        count++;
        curr = curr.next;
    }
    return result;
}