这道题本质是到蓄水池算法
image.png
https://leetcode.com/problems/linked-list-random-node/discuss/85659/brief-explanation-for-reservoir-sampling
https://zhuanlan.zhihu.com/p/29178293
先说一下普遍的情况吧:
要求对于一个流式数据,在不知道n的情况下,保证能够随机取出k个数。
只要我们保证对于每一个新的数,都以k/(k+i)的概率取值,直到k+i = n为止即可。
对于k = 1的情况:设一个额外的桶长度为1
取第一个数的概率为1;
取第二个数的概率为1/2,那么对于第一个数,依然留在桶里的概率为1 * 1/2(不取第二个数);
取第三个数的概率为1/3,那么对于第一个数,依然留在桶里的概率为1 * 1/2 * (1 – 1/3)=1/3(原先的概率乘以不取第三个数的概率),对于第二个数,依然留在桶里的概率为1/2 * (1 – 1/3)=1/3(同理);
取第四个数的概率为1/4,那么对于第一个数,1 * 1/2 * (1 – 1/3) * (1 – 1/4) = 1/4。第二个数,1/2 * (1 – 1/3) * (1 – 1/4) = 1/4。第三个数,1/3 * (1 – 1/4) = 1/4
以此类推,直到n,n中每个数取到的概率都是1/n。
对于k>1的情况:设桶长度为k
取前k个数的概率都是1;
取第k+1个数概率为k/(k + 1),对于第一个数,1/(k+1) (没有选中第k+1个数,1 - (k/k+1)) + k/(k + 1) * (k - 1)/k (选中了第k+1个数,但是替换的是其他的k-1个数,而非第一个数)。对于剩下的k-1个数同理;
取第k+2个数概率为k/(k + 2),同上;
以此类推,直到n,n中每个数取到的概率都是k/n。
再看下本题的内容,要求以等概率的形式取得链表中的每个节点的值,所以我们只要保证,随着链表往后遍历,每次都是以1/i的概率取得当前节点的值就可以了
class Solution {
ListNode p;
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
public Solution(ListNode head) {
this.p = head;
}
/** Returns a random node's value. */
public int getRandom() {
Random random = new Random();
ListNode cur = p;
int res = cur.val;
for (int i = 1; cur.next != null; i++) {
cur = cur.next;
if (random.nextInt(i + 1) == i) res = cur.val;
}
return res;
}
}
网友评论