【实用算法】如何随机打乱一个数组?

题目背景:

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组,要求每个数组排列返回的概率相同

常规思路:暴力求解

将数组中所有的数都放到数据结构 waiting 中,并初始化打乱后的数组shuffle。

循环n次,在第i次循环中(0 < i < n):

  • 在waiting中随机抽取一个数num,将其作为打乱后的数组shuffle的第i个元素;
  • 从waiting中移除num;
public int[] shuffle() {
    
    
		//初始化打乱后的数组
        int[] shuffled = new int[nums.length];
        //存放数组元素的列表list
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < nums.length; ++i) {
    
    
            list.add(nums[i]);
        }
        Random random = new Random();
        for (int i = 0; i < nums.length; ++i) {
    
    
            int j = random.nextInt(list.size());
            //
            shuffled[i] = list.remove(j);
        }
        System.arraycopy(shuffled, 0, nums, 0, nums.length);
        return nums;
	}
  • 时间复杂度:O(n2) 。需要遍历n个元素,每个元素需要O(n-k)的时间从nums中移除第k个元素。
  • 空间复杂度:O(n)。记录初始状态和临时的乱序数组均需要存储 n个元素。

更优思路:洗牌算法

通过洗牌算法可以实现数组的原地乱序:

循环n次,在第i次循环中(0 <= i < n):

  • 在 [ i , n ) 中随机抽取一个下标 j;
  • 将第 i 个元素与第 j 个元素交换。
public int[] shuffle() {
    
    
        Random random = new Random();
        for (int i = 0; i < nums.length; ++i) {
    
    
            int j = i + random.nextInt(nums.length - i);
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
        return nums;
    }
}
  • 时间复杂度:O(n) 。只需遍历n个元素即可打乱数组。
  • 空间复杂度:O(1)。原地操作,不需要额外空间。

猜你喜欢

转载自blog.csdn.net/weixin_43363720/article/details/121470686