1. 程式人生 > >[程式設計題] LeetCode上的Reservoir Sampling(蓄水池演算法)型別的題目

[程式設計題] LeetCode上的Reservoir Sampling(蓄水池演算法)型別的題目

目前LeetCode上Reservoir Sampling只有兩道:

給定一個單鏈表,隨機返回連結串列中一個節點的值,每個結點被選中的概率相等。

注意:
這個連結串列很大並且長度未知怎麼辦?

思路:蓄水池演算法

我們先一般化這個題目,改成:

給定一個單鏈表,隨機返回連結串列中K個節點的值,每個結點被選中的概率相等。

先說解法,再解釋:

  1. 從頭到尾遍歷連結串列
  2. 在遍歷1~k個結點時,直接放進結果集
  3. 遍歷到第i(i > k)個結點時,以k/i的概率決定是否將結點i放進結果集。如果不放,直接忽略。如果放,就從結果集的k個結點中隨機取出一個,替換為結點i
  4. 重複3,一直到尾節點。

這樣假設連結串列一共有n個結點,則連結串列中每個結點被選中進結果集的概率為k/n。

解釋:
假設結點i被選進結果集,有兩種情況:

  1. 1 <= i <= k :在遍歷到第k+1個結點之前,結點 i 是直接放進結果集的,所以概率為1。在遍歷到 k+1 個結點時,若果把結點 k+1 替換掉結點 i,那麼結點 i 就不在結果集中了,概率 =(將結點 k+1 放進結果集的概率) * (結果集中結點i被選中替換的概率)=(k/(k+1)) * (1/k) = 1/(k+1)。則結點 i 不被替換為結點 k+1 的概率為 1-(1/(k+1))= k/(k+1)。依次類推,當遍歷到尾節點 n 時,結點 i 不被替換為結點 n 的概率,也即最終留在結果集中的概率為 k/n。

  2. k < i <= n :前面的步驟說了以 k/i 的概率決定是否將結點 i 放進結果集,那麼當遍歷到結點 i+1 時,替換掉結點i的概率 =(將結點i+1放進結果集的概率)* (結果集中結點i被選中替換的概率)=(k/(i+1)) * (1/k)=1/(i+1)。則結點i不被替換為結點i+1的概率為1-(1/(i+1))= i/(i+1)。所以最終i留在結果集的概率 =(選中)* (不被替換)= (k/i) * (i/(i+1))= k/(i+1)。依次類推,當遍歷到尾節 n 時,結點 i 不被替換為結點 n 的概率,也即最終留在結果集中的概率為 k/n。

所以每個結點被選中進結果集的概率為 k/n。

public class Solution {

    ListNode head = null;
    Random random = null;

    public Solution(ListNode head) {
        this.head = head;
        this.random = new Random();
    }

    public int getRandom() {
        ListNode result = head;
        ListNode cur = head;
        for(int i = 1; cur != null; i++){
            if(random.nextInt(i) == i-1){
                result = cur; //因為這裡 K = 1 所以直接替換
            }
            cur = cur.next;
        }
        return result.val;
    }
}
public class Solution {
    int[] nums = null;
    Random r = null;

    public Solution(int[] nums) {
        this.nums = nums;
        r = new Random();
    }

    public int pick(int target) {
        int res = -1;
        int count = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == target){
                if(r.nextInt(++count) == 0){
                    res = i;
                } else {
                    continue;
                }
            }
        }
        return res;
    }
}