zoukankan      html  css  js  c++  java
  • leetcode 382 Linked List Random Node

    1 问题

    给定一个单链表,随机返回一个节点的值,保证每个节点被选择的概率相同。

    链表的长度未知,可能会非常大。

    2 测试方法

    一个节点数为7的单链表,返回1000000次值,统计每个节点被选择的频率。看每个节点被选择的频率是不是接近1/7。

    3 java随机数生成类java.util.Random

    Random(),创建一个新的随机数生成器。

    int nextInt(int bound),生成的整数在[0, bound)之间,生成这个区间里面的每个int的概率相同。

    nextInt(10),生成{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}这10个数中的一个,生成这10个数中的每个数的概率相同。

    4 reservoir sampling,水塘抽样

    这是一个在大数据流中随机抽样的问题,即在内存无法加载全部数据时,如何从未知大小的数据流中随机选取k个数据,保证每个数据被选择到的概率相同。

    水塘抽样的具体操作:

    第一,保留前k个数据;

    第二,当遍历到第i个数据的时候,以k/i的概率保留该数据,同时随机删除前k个数据中的一个。

    用归纳法证明:

    当数据总数为k的时候,每个数据被抽到的概率为1,显然是相同的。

    当数据总数为k+1的时候,当新加的第k+1的数据被以k/(k+1)的概率被选中的时候。原来的k个数据被选中的概率为:

    1/(k+1) + (k/(k+1))*(1-1/k))=k/(k+1)

    当数据总数为k+2的时候,当新加的第k+2的数据被以k/(k+2)的概率被选中的时候。原来的k+1个数据被选中的概率为:

    (k/(k+1)) * (2/(k+2) + k/(k+2) * (1-1/k))=k/(k+2)

    假设当数据总数为N的时候,按照水塘操作每个元素被选中的概率为k/N,那么当新来了一个新的元素N+1的时候,该元素被选中的概率为k/(N+1),其它元素被选中的概率为

    (k/N)*(1 - k/(N+1) + k/(N+1)*(1 - 1/k))=k/(N+1)

    所以,当数据总数为N+1的时候,所有数据被选中的概率也是一样的。

    因此,水塘操作可以保证数据流中每个元素被选中的概率相同。

    5 当k=1的时候就是Linked List Random Node问题

    第一,保留第一个数据;

    第二,当遍历到第i个数据的时候,以1/i的概率保留该数据用来替换原来的数据。

    比如来了第4个数据,前三个数据被选中的概率是

    在上个数据到来时被选中的概率 * 第4个数据没有被选中的概率 = (1/3) * (3/4) = 1/4

    6 编码实现

    private static int getRandomNode(ListNode head) {
    int i = 1;
    ListNode listNode = head;
    int x = 0;
    Random random = new Random();
    while (listNode != null) {
    if (random.nextInt(i) == (i - 1)) {
    x = listNode.get();
    }

    listNode = listNode.next;
    i++;
    }

    return x;
    }

    7 测试

    HashMap<Integer, Integer> choosenCntMap = new HashMap<Integer, Integer>();

    for (int j = 0; j < 1000000; j++) {
    int x = getRandomNode(head);
    if (choosenCntMap.containsKey(x)) {
    int cnt = choosenCntMap.get(x);
    cnt++;
    choosenCntMap.put(x, cnt);
    } else {
    choosenCntMap.put(x, 1);
    }
    }

    for (Integer x : choosenCntMap.keySet()) {

    int cnt = choosenCntMap.get(x);

    System.out.println("x:" + x + "----" + "cnt:" + cnt);
    }

    x:2----cnt:142675
    x:3----cnt:142428
    x:5----cnt:142322
    x:7----cnt:143076
    x:8----cnt:143200
    x:10----cnt:143340
    x:11----cnt:142959

    每个节点被选择的概率都接近1/7。

    10 参考资料

    10.1 https://zhanghuimeng.github.io/post/leetcode-382-linked-list-random-node/

    10.2 https://gregable.com/2007/10/reservoir-sampling.html

    10.3 https://leetcode.com/problems/linked-list-random-node/discuss/85659/Brief-explanation-for-Reservoir-Sampling

  • 相关阅读:
    linux_ssh用户枚举猜测
    Nginx 主配置文件参数详解
    OSI七层模型
    linux-Python升级安装
    qt多线程
    python 对串口的操作
    keil 下模拟u-boot的cmd功能
    <转载>CentOS 6.3下Samba服务器的安装与配置
    Magento开发完整指南
    飞书信(Facebook Messenger)是什么?
  • 原文地址:https://www.cnblogs.com/hustdc/p/10948151.html
Copyright © 2011-2022 走看看