• LeetCode398随机数索引


    原题链接:【398. 随机数索引】:https://leetcode-cn.com/problems/random-pick-index/

    题目描述:

    给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。

    注意:

    数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。

    示例:

    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);

    解题思路:

    解法1:哈希表。

    暴力解法。使用哈希表存储数组中每个元素值到其所有的下标索引的映射。

    即key为元素值,value为元素值对应的索引下标列表。

    再用random函数,从下标列表中随机选择一个索引返回即可。

    这个解法非常占用额外空间O(N),如果数组很大,很容易oom。

    解法2:动态数组存储索引列表。

    定义一个存储索引下标的索引列表indexList,用动态数组,方便扩展。

    遍历数组,如果元素跟target目标值相等,则把索引值添加进indexList列表。

    取随机数,rand范围为【0,indexList.length ),然后将对应位置的索引值返回即可。

    概率为:1 / indexList.length

    这里相比上一种解法,已经降了一个数量级,但是如果N很大,或者数字分布不均匀,indexList也可能非常大,也非常占用额外空间,空间消耗也可能会达到O(N),可能会oom。

    解法3:蓄水池抽样算法。

    算法是:要从超大数组N中取k个元素,这里是取一个元素,即K=1

    那么每次只取一个样本值,那么逐个遍历数组,当遇到第i个数时,就以 1 / i 的概率抽取它做样本值,以(i - 1)/ i 的概率保留原来的样本值,不替换。

    我们不需要保存索引下标列表,只需要一个变量cnt计数,统计匹配到了多少个相同的数字,方便后去取随机数,算概率。

    遍历数组,如果元素跟target目标值相等,则cnt++,计数器+1。

    然后取随机数,rand范围为【1,cnt】。

    因为这里只取一个数,就是K=1的案例。

    所以如果rand == 1,则取当前元素的下标,替换为结果值。

    如果rand > 1,则保留之前的结果,继续往下走。知道结束返回结果值。

    特点:这里只需要遍历一遍数组,而且没有额外的空间消耗,只有一个计数器变量cnt。空间复杂度为O(1)。(非常推荐)

    题解代码:

    代码如下:(java版)

    //给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。 
    //
    // 注意: 
    //数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。 
    //
    // 示例: 
    //
    // 
    //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);
    // 
    // Related Topics 水塘抽样 哈希表 数学 随机化 
    //  125  0
    
    
    import java.util.Random;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        // 需要把nums传递给pick方法,所以我们需要定义一个类变量
        int[] nums;
        public Solution(int[] nums) {
            this.nums = nums;
        }
        
        public int pick(int target) {
            // 定义一个变量,存储结果值
            int result = 0;
            // 定义一个变量cnt,计数器,标记匹配到了几个相同的数字,方便后面取随机数范围
            int cnt = 0;
            // 循环遍历链表
            for (int i=0; i<nums.length; i++) {
                // 匹配跟target元素是否相等
                if (nums[i] == target) {
                    // 匹配到了,则计数器+1
                    cnt++;    
                    // 取随机数rand范围为[1, cnt],注意这里边界是cnt
                    int rand = new Random().nextInt(cnt) + 1;
                    // 取每个数的概率都是 1 / cnt,也就是说有1 / cnt的概率,更新结果下标值
                    if (rand == 1) {
                        // rand等于1,则取当前元素的下标,更新结果值
                        result = i;
                    }
                }
    
            }
    
            // 返回结果下标
            return result;
        }
    }
    
    /**
     * Your Solution object will be instantiated and called as such:
     * Solution obj = new Solution(nums);
     * int param_1 = obj.pick(target);
     */
    //leetcode submit region end(Prohibit modification and deletion)
    
    
  • 相关阅读:
    ANSYS Workbench 16.0超级学习手册(附光盘)
    自控力(套装共2册)
    超实用记忆力训练法
    大愿法师“幸福人生”系列丛书——禅心商道
    高情商沟通术(写给所有深受社交困扰的人)
    青年职业形象管理手册(高等职业教育在线开放课程新形态一体化规划教材)
    1019. 括号匹配
    1026. 高精度除法
    1018. A+B Problem Revisited
    1015. 高精度乘法
  • 原文地址:https://www.cnblogs.com/doublexi/p/15666221.html
Copyright © 2020-2023  润新知