[382]. 链表结点

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[382]. 链表结点相关的知识,希望对你有一定的参考价值。

[382]. 链表结点

 


题目

传送门:https://leetcode.cn/problems/linked-list-random-node/


 


算法设计:数组取摸

思路:单链表不能随机访问,数组才可以。所以,我们用一个数组保存链表数据,再对数组求模实现随机选取。

class Solution 
    vector<int> arr;
public:
    Solution(ListNode *head) 
        while (head) 
            arr.push_back(head->val);      // 数组保存
            head = head->next;
        
    
    int getRandom() 
        return arr[rand() % arr.size()];   // 随机选取
    
;

时间复杂度: θ ( n ) \\theta(n) θ(n)

空间复杂度: θ ( n ) \\theta(n) θ(n)
 


算法设计:蓄水池抽样

蓄水池抽样,是一种用于解决数据流的多次公平随机采样算法。

  • 多次、等概率、随机采样:字面意思,拒绝采样、洗牌算法也可以实现。
  • 数据流:不是一次性给出所有数据,而是一个数据流,不知道到底有多少数据。蓄水池算法的特点,在于数据流上。

数据是不固定的,蓄水池抽样的思路是,建立一个能存储 k 个元素的蓄水池。

k 是可以动态调整的,可大可小。

步骤:

  • 从数据流中,放入 k 个元素进入蓄水池 [0, k-1]
  • 从数据流中第 k + 1 个数据开始算,处理第 i (k + 1)个元素,随机生成 [0, i-1] 之间的索引 j
  • 如果 j 在 [0, k-1] 之间,则替换蓄水池[j],否则,扔掉

这样就能保证,每个元素都会以等概率( k k + 1 \\frackk+1 k+1k)出现在蓄水池中。

【数学证明】(可跳过)

前 k 个数据都在蓄水池内,从第 k + 1 个新数据开始。

处理完第 k+1 个元素,蓄水池相当于见到了 k+1 个元素,每个元素都是以 k k + 1 \\frackk+1 k+1k 出现在蓄水池中。

从数据流中第 k + 1 个数据开始算,在 [0, k] 之间生成一个索引,如果在蓄水池范围内 [0, k-1],则替换蓄水池中的索引位置。

  • 新元素出现在蓄水池的概率是: k k + 1 \\frackk+1 k+1k,只有在最后一个索引位置才不能出现在蓄水池

对于天生(前 k 个元素)就处于蓄水池中的元素,每个元素被替换的概率是: 1 k + 1 \\frac1k+1 k+11

  • 反过来说,经过调整后,原本元素都有 k k + 1 \\frackk+1 k+1k 的概率继续留在蓄水池

所以说,每个数据出现在蓄水池的概率是 k k + 1 \\frackk+1 k+1k

class Solution 
    ListNode *head;
public:
    Solution(ListNode *head) 
        this->head = head;
    
    int getRandom() 
        int i = 1;
        // i 用于记录新的元素的索引,i = 1 表示蓄水池只能存储 1 个元素 
        int ans = head->val;
        // 提前把第 1 个元素放入蓄水池
        for (auto node = head->next; node != nullptr; node = node->next, i ++) 
        // 新元素是从数据流第 2 个元素开始取,如果链表不为空,再接着取下一个元素 | 新取元素索引 + 1,记录目前是第几个元素
        	int j = random() % (i + 1);     
        	// 随机生成 [0, k] 之间的索引
			if ( j == 0 ) ans = node->val;  
			// 如果 j 处于蓄水池范围内,这里的蓄水池容量为 1,范围是 [0, 0]
        
        return ans;
    
;

时间复杂度: θ ( 1 ) \\theta(1) θ(1)

空间复杂度: θ ( 1 ) \\theta(1) θ(1)

以上是关于[382]. 链表结点的主要内容,如果未能解决你的问题,请参考以下文章

codeforce 382 div2 E —— 树状dp

382. Linked List Random Node

Reservoir Sampling - 蓄水池抽样算法

蓄水池算法(链表随机节点)

蓄水池算法(链表随机节点)

LeetCode Algorithm 382. 链表随机节点