text: # [382. Linked List Random Node](https://leetcode.com/problems/linked-list-random-node/)
  
  # 思路
  给定一个链表，让随机返回一个节点，最直接的方法就是先统计出链表的长度，然后根据长度随机生成一个位置，然后从开头遍历到这个位置即可。
  最直接的思路需要两次遍历链表，此题其实是著名的[蓄水池采样（Reservoir Sampling）](https://zh.wikipedia.org/wiki/%E6%B0%B4%E5%A1%98%E6%8A%BD%E6%A8%A3)算法要解决的问题（[398题](https://leetcode.com/problems/random-pick-index/)也是用此算法），只需要一次遍历。下面介绍这个算法。
  
  从一个群体中随机抽样出k个个体是经常会遇到的抽样问题，当事先不知道群体大小时，我可以先遍历一次数据计算出数据量N, 然后再按照上述的方法进行采样即可。
  这当然可以，但是并不好，因为有时候数据只有一次访问的机会，或者很难确定数据规模有多大，例如当内存无法加载全部数据时，如何从包含未知大小的数据流中随机选取k个数据，并且要保证每个数据被抽取到的概率相等。
  
  假设需要从数据流采样的数量的为k，蓄水池采样算法的过程如下：
  1. 首先构建一个可容纳k个元素的数组（形象地理解为蓄水池)，先将数据流的前k个元素放入数组中；
  2. 然后遍历到第 k+i 个元素时，以 `k/(k+i)` 的概率被替换进数组中（原数组中的元素被替换的概率是相同的, 都是 `1/k`）。 
  
  当遍历完所有元素之后，数组中存放的k元素即为所采的样本。
  
  证明如下：
  
  遍历到第 k+i 个元素时，水池数组中的元素保留下来的概率 = 1 - `p(第k+i个元素要替换进数组)`x`p(替换掉数组中这个元素)` = 1 - `k/(k+i)`x`1/k` = (k+i-1)/(k+i)。
  所以
  
  1. 数据流前k个元素到最后还在水池中的概率是: `k/(k+1)` x `(k+1)/(k+2)` x ... x `(n-1)/n` = `k/n`。
  
  数据流第 k+i 个元素要想最后在水池中，那么遍历到它时它要被替换进水池中（概率=`k/(k+i)`）而且后面一直不被替换掉，所以
  
  2. 数据流第 k+i 个元素到最后在水池中的概率是: `k/(k+i)` x `(k+i)/(k+i+1)` x ... x `(n-1)/n` = `k/n`。
  
  综合上面两个结论可知，数据流中所有元素最后留在水池中的概率均为`k/n`，得证。
  
  此题是蓄水池抽样算法的特殊情况：k=1。有了上面的分析就不难写出代码了。
  
  # C++
  ``` C++
  class Solution {
  private:
      ListNode *root;
  public:
      /** @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) {
          root = head;
      }
      
      /** Returns a random node's value. */
      int getRandom() {
          ListNode *p = root;
          int res = -1;
          
          int count = 1;
          while(p){
              if(rand() % count == 0) res = p -> val;
              p = p -> next; count++;
          }
          
          return res;
      }
  };
  
  ```
  
  