Given an array of integers with possible duplicates, randomly output the index of a given target number. You can assume that the given target number must exist in the array. Note: The array size can be very large. Solution that uses too much extra space will not pass the judge. Example: int[] nums = new int[] {1,2,3,3,3}; Solution solution = new Solution(nums); // pick(3) should return either index 2, 3, or 4 randomly. Each index should have equal probability of returning. solution.pick(3); // pick(1) should return 0. Since in the array only nums[0] is equal to 1. solution.pick(1);
設計一個數據結構,使得從該數據結構中查詢一個數字時,可以以等機率返回該數字所在的任何下標。額外的要求是隻要佔用O(1)的額外的空間複雜度。java
其實要是想在O(1)的時間內完成隨機數的獲取,只須要緩存每一個數字出現的下標,可是這意味着須要先對數據進行遍歷,而且還須要O(n)的空間來額外存儲數字下標的集合。這違背了題目意思。因此咱們只能每次在獲取數字的時候來統計數字出現的次數,而後針對次數獲取隨機數下標。算法
代碼以下:緩存
private int[] nums; private Random r; public Solution(int[] nums) { this.nums = nums; this.r = new Random(); } public int pick(int target) { int count = 0; int result = -1; for(int i = 0 ; i<nums.length ; i++) { if(target == nums[i]) { count++; } } int index = r.nextInt(count); for(int i = 0 ; i<nums.length ; i++) { if(target == nums[i]) { index--; if(index == -1){ result = i; break; } } } return result; }
有沒有可能在一次的遍歷中,找到這個隨機下標呢?這就涉及到一個機率問題,即當我在遍歷過程當中遇到這個數字時,我是否須要選擇它做爲個人結果值。數據結構
首先介紹一下蓄水池抽樣算法。蓄水池抽樣算法主要對應的是這樣的一個問題,假設有一個很是大的數據集,或者是一個以流的形式做爲輸入的數據集,但願從中選擇K個樣本,此時咱們可能沒法把全部的樣本都放在內存中,所以只能保存一個大小爲K的集合,每次遇到一個數據時,決定是否將其放入樣本中。dom
所以,假設當前遇到的數據總共有N個,若是N小於K,則每一個數據被選中的機率爲1,若是N>K,則每一個數據被選中的機率爲K/N,舊樣本中數據被保留的機率爲1 - K(N-K)/N
即K/N,所以每個舊的數據被替換掉的機率爲1/N。this
按照這個思路,咱們在下面的遍歷時,每遇到一個新元素,就判斷當前選中的元素是否會被該元素替換掉便可。spa
private int[] nums; private Random r; public Solution(int[] nums) { this.nums = nums; this.r = new Random(); } public int pick(int target) { int count = 0; int result = -1; for(int i = 0 ; i<nums.length ; i++) { if(nums[i] == target) { int index = r.nextInt(++count); if(index == 0) { result = i; } } } return result; }