Get a random element in single direction linked li

2019-03-26 01:24发布

问题:

I have a single direction linked list without knowing its size.

I want to get a random element in this list, and I just have one time chance to traverse the list. (I am not allowed to traverse twice or more)

What’s the algorithm for this problem? Thanks!

回答1:

This is just reservoir sampling with a reservoir of size 1.

Essentially it is really simple

  1. Pick the first element regardless (for a list of length 1, the first element is always the sample).
  2. For every other element with probability 1/n where n is the number of elements observed so far, you replace the already picked element with the current element you are on.

This is uniformly sampled, since the probability of picking any element at the end of the day is 1/n (exercise to the reader).



回答2:

This is probably an interview question.Reservoir sampling is used by data scientist to store relevant data in limited storage from large stream of data.

If you have to collect k elements from any array with elements n, such that you probability of each element collected should be same (k/n), you follow two steps,

1) Store first k elements in the storage. 2) When the next element(k+1) comes from the stream obviously you have no space in your collection anymore.Generate a random number from o to n, if the generated random number is less than k suppose l, replace storage[l] with the (k+1) element from stream.

Now, coming back to your question, here storage size is 1.So you will pick the first node,iterate over the list for second element.Now generate the random number ,if its 1, leave the sample alone otherwise switch the storage element from list



回答3:

This question can be done using reservoir sampling. It is based on choosing k random items out of n items, but here n can be very large(which doesn't has to fit in memory!) and (as in your case) unknown initially.

The wikipedia has an understandable algorithm which i quote below:

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

The question requires only 1 value so we take k=1.

C implementation :

https://ideone.com/txnsas



回答4:

This is the easiest way that I have found, it works fine and is understandable:

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;
}