leetcode 382——链表中的随机节点(蓄水池抽样法)

题目描述:
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?

解题思路:
这道题最简单的解法应该是用一个vector保存所有节点的值,然后在vector中求取随机值就相当容易了,但如进阶条件所说,当链表十分大的时候时间复杂度和空间复杂度都是O(n).

这时我们就要用到蓄水池抽样法:
查了半天蓄水池采样的资料,看的一脸懵。
很多材料上来就告诉你第 i 个节点要以1/i 概率选取,然后再去证明这个值是对的。但问题是我要怎么想到这个值呢?

这时候就要用到终局思维了:当走到最后一个节点 n 时,要保证最后一个节点被选取概率是1/n,那么这时就以1/n 概率选取这个值,如果n被选中就用n替换之前被选取的值。
这时候再往前推一步,要保证 n-1最终被选取的概率是也是1/n,那么当走到n-1时,要以什么概率选取n-1?
假设这个概率是 x,那么 n-1最终被选取的概率就是 (n-1) / n * x,其含义是:被选点在[1, n-1]内的概率 * 在[1, n-1]范围内n-1被选中的概率
那么我们就推出来 x * (1-1/n) == 1/n,x 的值就是1/n-1。有没有霍然开朗的感觉?
以上思维方式,也可以推广到选取 k 个,稍微一点改变是,在看 n-1最终是否被保留下来时,等式是
x * (1-k/n * 1/k) == k / n,k/n * 1/k的含义是 n被选取了,并且将集合中的 n-1给替换掉了(概率就是从 k 个里选1个),
再解释一下就是: n-1先被以 x 的概率选取,而后又被 n替换掉。
因此n-1最终保留在集合中的概率就是 x - x * k/n * 1/k,这个概率要等于 k/n。

有了以上推导,终于能放心的使用1/i 这个概率值选取第 i 个节点了。代码很简单。

class Solution {
public:
    ListNode* head_;
    
    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    Solution(ListNode* head) {
        head_ = head;
    }
    
    /** Returns a random node's value. */
    int getRandom() {
        if (head_ == nullptr)
            return -1;
        
        int res = head_->val;
        ListNode* cur = head_->next;
        int count = 1;
        while (cur != nullptr)
        {
            ++count;
            if (rand() % count == 0)    //以 1/count 概率选取当前节点
                res = cur->val;
            cur = cur->next;
        }
        
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值