題目描述
給定一個可能含有重複元素的整數數組,要求隨機輸出給定的數字的索引。 您可以假設給定的數字一定存在於數組中。
注意:
數組大小可能非常大。 使用太多額外空間的解決方案將不會通過測試。
題目示例
int[] nums = new int[] {1,2,3,3,3};
Solution solution = new Solution(nums);
// pick(3) 應該返回索引 2,3 或者 4。每個索引的返回概率應該相等。
solution.pick(3);
// pick(1) 應該返回 0。因爲只有nums[0]等於1。
solution.pick(1);
這題目中有一個地方需要注意,每一個索引返回的概率都是相同的。比較笨的方法就是將與 target 相等的元素存放到一箇中間集合中,最後從中間集合隨機取一個。第二種辦法就是利用蓄水池抽樣法來解決這個問題。
解法一:使用中間集合
我們先從頭到尾遍歷數組,將與 target 相等元素的索引,集中存儲到中間集合 list 中,最後再從中間集合中隨機返回一個元素索引。
1、解題代碼:
class Solution {
private int[] nums;
public Solution(int[] nums) {
this.nums = nums;
}
public int pick(int target) {
// 開闢一箇中間集合
List<Integer> list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
// 找出與target相等的元素
if (nums[i] == target) {
list.add(i);
}
}
// 如果只存在一個,就直接返回
if (list.size() == 1) return list.get(0);
// 如果有兩個以上,隨機取一個
return getRandomIndex(list);
}
/**
* 隨機給出下標
*
* @param list 給定元素的索引集合
* @return
*/
public int getRandomIndex(List<Integer> list) {
return list.get(new Random().nextInt(list.size()));
}
}
複雜度分析
時間複雜度:需要遍歷一次數組,所以時間複雜度是O(N)
空間複雜度:使用了 ArrayList ,ArrayList 的大小與數組有關,所以空間複雜度爲O(N)
解法二:蓄水池抽樣算法
這是我在 leetcode 上看到的解法,蓄水池抽樣算法好像是一種專業的算法,關於算法的詳情,大家就自行百度,我也不是太清楚,這裏我就簡單的描述:給定一個數據流,數據流長度N很大,且N直到處理完所有數據之前都不可知,請問如何在只遍歷一遍數據(O(N))的情況下,能夠隨機選取出m個不重複的數據。符合我們的題意,這裏我就直接給出解題代碼,具體的也不是很清楚,不過刷題不就是擴寬思維嗎?
1、解題代碼
class Solution {
private int[] nums;
public Solution(int[] nums) {
this.nums = nums;
}
public int pick(int target) {
// 返回下標,沒找到就返回-1
int index = -1;
// 記錄查找到的個數
int count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
count++;
// 隨機返回,new Random().nextInt() % count == 0 作爲概率
if (new Random().nextInt() % count == 0) index = i;
}
}
return index;
}
}
複雜度分析
時間複雜度:需要遍歷一次數組,所以時間複雜度是O(N)
空間複雜度:沒有使用新的集合,所以空間複雜度爲O(1)
最後
歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。