zoukankan      html  css  js  c++  java
  • LeetCode——打乱数组:洗牌算法

    Q:打乱一个没有重复元素的数组。

    示例:

    // 以数字集合 1, 2 和 3 初始化数组。
    int[] nums = {1,2,3};
    Solution solution = new Solution(nums);
    
    // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
    solution.shuffle();
    
    // 重设数组到它的初始状态[1,2,3]。
    solution.reset();
    
    // 随机返回数组[1,2,3]打乱后的结果。
    solution.shuffle();
    

    A:
    以下部分引用自@labuladong的算法小抄:
    此类算法都是靠随机选取元素交换来获取随机性,直接看代码(伪码),该算法有 4 种形式

    // 得到⼀个在闭区间 [min, max] 内的随机整数
    int randInt(int min, int max);
    // 第⼀种写法
    void shuffle(int[] arr) {
        int n = arr.length();
        /******** 区别只有这两⾏ ********/
        for (int i = 0 ; i < n; i++) {
        // 从 i 到最后随机选⼀个元素
            int rand = randInt(i, n - 1);
            swap(arr[i], arr[rand]);
        }
    }
    // 第⼆种写法
        for (int i = 0 ; i < n - 1; i++)
            int rand = randInt(i, n - 1);
    // 第三种写法
        for (int i = n - 1 ; i >= 0; i--)
            int rand = randInt(0, i);
    // 第四种写法
        for (int i = n - 1 ; i > 0; i--)
            int rand = randInt(0, i);
    

    第二个和第四个写法都是减少最后一次1*1的交换。
    分析洗牌算法正确性的准则:产⽣的结果必须有 (n!) 种可能,否则就是错误的。这个很好解释,因为⼀个⻓度为 n 的数组的全排列就有 (n!) 种,也就是说打乱结果总共有 (n!) 种。算法必须能够反映这个事实,才是正确的。
    如果读者思考过洗牌算法,可能会想出如下的算法,但是这种写法是错误的:

    void shuffle(int[] arr) {
        int n = arr.length();
        for (int i = 0 ; i < n; i++) {
            // 每次都从闭区间 [0, n-1] 中随机选取元素进⾏交换
            int rand = randInt(0, n - 1);
            swap(arr[i], arr[rand]);
        }
    }
    

    现在你应该明⽩这种写法为什么会错误了。因为这种写法得到的所有可能结果有 (n^n) 种,⽽不是 (n!) 种,⽽且 (n^n) 不可能是 (n!) 的整数倍。

    代码:

        private int[] array;
        private int[] original;
    
        Random rand = new Random();
    
        public Solution(int[] nums) {
            array = nums;
            original = nums.clone();
        }
    
        /**
         * Resets the array to its original configuration and return it.
         */
        public int[] reset() {
            array = original;
            //这句是必然的,需要把array和original的地址分开,否则array一变,original也变
            original = original.clone();
            return original;
        }
    
        /**
         * Returns a random shuffling of the array.
         */
        public int[] shuffle() {
            int n = array.length;
            for (int i = 0; i < n; i++) {
                int index = rand.nextInt(n - i) + i;
                int temp = array[index];
                array[index] = array[i];
                array[i] = temp;
            }
            return array;
        }
    
  • 相关阅读:
    委托(delegate)的三种调用方式:同步调用,异步调用,异步回调(转载)
    C#异步:实现一个最简单的异步
    关于Thread.IsBackground属性的理解(转载)
    C# 中的多线程(转载)
    个人对AutoResetEvent和ManualResetEvent的理解(转载)
    C#线程系列讲座(4):同步与死锁
    Nginx location 配置踩坑过程分享
    微信扫码登录网页实现原理
    负载均衡SLB
    Tomcat学习
  • 原文地址:https://www.cnblogs.com/xym4869/p/12725704.html
Copyright © 2011-2022 走看看