LeetCode382. 链表随机节点的三种解法:数组储存法、随机步长法、蓄水池抽样算法

题目概述:

在这里插入图片描述
链接:点我做题

题解

一、数组接收暴力算法

  由于链表不支持随机访问,单链表只支持顺序访问,我们要用rand()等概率的随机取一个0~length - 1的随机值然后直接访问是不可以的,为了随机访问我们可以先拷到一个数组里。

class Solution {
public:
    Solution(ListNode* head) 
    {
        ListNode* cur = head;
        while (cur != nullptr)
        {
            _vec.push_back(cur->val);
            cur = cur->next;
        }
    }
    
    int getRandom() 
    {
        return _vec[rand() % _vec.size()];
    }
private:
    vector<int> _vec;
};

时间复杂度: O ( n ) 、 O ( 1 ) O(n)、O(1) O(n)O(1)
空间复杂度: O ( n ) O(n) O(n)

二、随机生成步长直接往前走

  我们在初始化的时候,顺便记录一下链表的长度_sz,然后用rand()%_sz等概率的取得一个 [ 0 , _ s z − 1 ] [0,\_sz - 1] [0,_sz1]随机数,然后从头往前走随机数步,返回对应位置的值。

class Solution {
public:
    Solution(ListNode* head) 
    {
        _head = head;
        while (head != nullptr)
        {
            _sz++;
            head = head->next;
        }
    }
    int getRandom() 
    {
        ListNode* cur = _head;
        int walks = rand() % _sz;
        while (walks--)
        {
            cur = cur->next;
        }
        return cur->val;
    }
private:
    ListNode* _head;
    int _sz = 0;
};

时间复杂度: O ( n ) 、 O ( n ) O(n)、O(n) O(n)O(n)
空间复杂度: O ( 1 ) O(1) O(1)

三、蓄水池抽样算法

  每次要返回随机结点的值时,就遍历一遍链表。
  对遍历到第i个结点,利用rand()%i随机等可能的取一个 [ 0 , i ) [0,i) [0,i)的整数
  如果取到0,就让返回值变成当前结点的值
  然后接着遍历下一个结点,重复上述过程
  由概率论的知识,第i个结点的值成为返回值的概率等于 P ( 第 i 个 结 点 成 为 返 回 值 的 概 率 ) = P ( 遍 历 到 i 时 从 [ 0 , i ) 选 中 了 0 且 第 i + 1 个 结 点 没 有 被 选 中 且 . . . 且 第 n 个 结 点 没 被 选 中 ) P(第i个结点成为返回值的概率) \\= P(遍历到i时从[0,i)选中了0 且 第i+1个结点没有被选中 且 ... 且 第n个结点没被选中) P(i)=P(i[0,i)0i+1...n)
  由于这些事件独立,所以有:
  上式=1/i * (1 - 1/(i + 1)) * … *(1 - 1/n)
  =1/i * i/(i + 1) *… * (n - 1)/n
  =1/n
  显然任意一个结点成为返回值的概率是相等的。

class Solution {
public:
    Solution(ListNode* head) 
    {
        this->_head = head;
    }
    
    int getRandom() 
    {
        ListNode* cur = _head;
        int ret = 0;
        int i = 1;
        //对遍历到第i个结点,随机等可能的取一个[0,i)的整数
        //如果取到0,就让返回值变成当前结点的值
        //然后接着遍历下一个结点,重复上述过程
        //由概率论的知识,第i个结点的值成为返回值的概率为
        //P(第i个结点成为返回值的概率) = 
        //P(遍历到i时从[0,i)选中了0 && 第i+1个结点没有被选中 && ... && 第n个结点没被选中)
        //由于这些事件独立,所以
        //上式=1/i * (1 - 1/(i + 1)) * ... *(1 - 1/n)
        //=1/i * i/(i + 1) *... * (n - 1)/n
        //=1/n
        //显然是等概率的
        while (cur != nullptr)
        {
            if (rand() % i == 0)
            {
                ret = cur->val;
            }
            cur = cur->next;
            i++;
        }
        return ret;
    }
private:
    ListNode* _head;
};

时间复杂度: O ( 1 ) 、 O ( n ) O(1)、O(n) O(1)O(n)
空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值