1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { private Map<Integer, List<Integer>> numIdxMap; private Random rand;
public Solution(int[] nums) { numIdxMap = new HashMap<>(); rand = new Random(); for (int i = 0; i < nums.length; i++) { numIdxMap.putIfAbsent(nums[i], new ArrayList<>()); numIdxMap.get(nums[i]).add(i); } }
public int pick(int target) { List<Integer> targetIdxList = numIdxMap.get(target); return targetIdxList.get(rand.nextInt(targetIdxList.size())); } }
|
最直接的, 用map来存每个元素以及它们都在哪些index出现了.
等到需要pick的时候, 随机生成该元素从0到出现次数(exclusive)范围内的数字, 然后去map中该元素对应存该元素出现在哪些index的list中去访问以这个随机生成的数字为index的元素值. 感觉描述有些多余. 直接看代码吧.
时间复杂度: 初始化是O(N), pick是O(1)
空间复杂度: O(n) 用了map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { private int[] nums; private Random rand;
public Solution(int[] nums) { this.nums = nums; rand = new Random(); }
public int pick(int target) { int count = 0, result = -1; for (int i = 0; i < nums.length; i++) { if (nums[i] == target) { count += 1; if (rand.nextInt(count) == 0) { result = i; } } } return result; } }
|
这个叫reservoir sampling. 这个解法会超时, 算是学习一下新的东西. 假设我们要pick一个数字, 这个数字有n个duplicates. 那么我们假设要挑到第m个(1-index), 那概率是多少? 按照我们的逻辑, 当我们count到第m个的时候, 我们让count + 1. 此时count == m. 那么如果要摇到第m个, 此时就需要让rand产生0. 此时概率为1 / m因为范围是[0, m). 然后我们遇到第m + 1个duplicates的时候, count继续加1变为m + 1. 如果我们要保留之前摇到的m, 我们就需要让此时摇到初0之外的数字, 这样上一次摇的的m就得以保留. 那么此时这样的概率就是(1 / m) * (m / m + 1). 第一项是上一次摇到m的概率乘上现在不摇到0个概率, 于是是1/ m + 1. 以后也是一样的道理来推. 这样m可以适用于任何位置上的duplicate. 思路大概就是这样的.
时间复杂度: 初始化O(1). pick是O(n)
空间复杂度: O(1) 如果不算传进来的输入的话