382. Linked List Random Node

28 篇文章 0 订阅
17 篇文章 0 订阅

Given a singly linked list, return a random node’s value from the linked list. Each node must have the same probability of being chosen.

Follow up:
What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space?

Example:

// Init a singly linked list [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);

// getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.
solution.getRandom();

初始想法,采用Math.random()函数,但是后期提交有例子未通过,遂改为利用Random类提供随机数,提交通过,但是这种方法需要知道数链长度且空间要求高。Math和Random的两种随机方法建议采用Random类的随机方法,具体原因可见:Math.random() versus Random.nextInt(int)
故改为第二种方法:水塘抽样法。该方法的原理可参见维基百科数据工程师必知算法:蓄水池抽样Brief explanation for Reservoir Sampling。具体思路如下:
水塘容量k即要保留的数据,也可以理解为采样值。在本题为1。
现在考虑1个输入,则水塘保留,故保留概率P=1/1;考虑2个输入,水塘只能保留1个,所以对于新加入的“2”保留其的概率为1/2;现在考虑3个输入,保留“3”的概率为1/3,这样对于3个输入来说,3的保留概率为1/3正好,现在看看三输入下的前两个数字被保留的概率,对“1”和“2”来说,前一轮被保留的概率为1/2,这一轮被保留的概率为(1-1/3=2/3),所有总的保留概率为1/2×2/3=1/3。正好满足概率均等。所以推论,对k=1的情况,第i个数保留概率为1/i。

  • 1个输入 1(1/1)
  • 2个输入 1,2(1/2)
  • 3个输入 1,2(删去),3(1/3)

以上就是该题的解题思路,再拓展一下,对于要保留(采样)k个数据,推论第i个数保留概率为k/i,例子(k=2)如下:一个数,200%保留;两个数100%保留;三个数,保留“3”概率为2/3;”1”保留的概率为:1/3(“3”被舍弃即”1,2“保留概率)+2/3(“3”保留下来时概率)×1/2(“1,2”挑一个踢走)=2/3,同理得“2”的概率为2/3;四个数,“4”为2/4,剩下两个数的都为:2/3(上轮保留概率)×(2/4(“4”被踢走概率)+2/4(“4”留下概率)×1/2(二选一被踢走概率))=2/4=1/2;总结起来就是:当进行到第i个数时,第i个数的概率为 k/i ,之前数保留概率为 [k/(i1)][(1k/i)+(k/i)[(k1)/k]]=k/i

  • 1个输入 1(2/1)
  • 2个输入 1,2(2/2)
  • 3个输入 1,2(删去),3(2/3)
  • 4个输入1(删去)2(删去),3,4(2/4)

初始思想的算法如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {

    ArrayList<Integer> list;

    /** @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) {
        list = new ArrayList<>();
        ListNode i = head;
        while(i != null){
            list.add(i.val);
            i = i.next;
        }
    }

    /** Returns a random node's value. */
    public int getRandom() {
        int size = list.size();
        Random random = new Random();
        int index = random.nextInt(size);
        return list.get(index);
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */

水塘算法如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ListNode h;

    /** @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) {
        h = head;
    }

    /** Returns a random node's value. */
    public int getRandom() {
        ListNode cur = h;
        int res = cur.val;
        Random random = new Random();
        for(int count = 1; cur != null; count++){

            if(random.nextInt(count) == 0) res = cur.val;
            cur = cur.next;
        }
        return res;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值