当前位置: 首页 > news >正文

洗牌算法讲解——力扣384.打乱数组

在这里插入图片描述


【LeetCode 384】打乱数组(Java 详细题解 + Fisher–Yates 洗牌算法讲解)

一、题目描述

给定一个没有重复元素的整数数组 nums,设计一个算法实现数组的随机打乱,使得所有排列出现的概率相等。

你需要实现一个类 Solution,包含以下三个方法:

class Solution {public Solution(int[] nums) { } // 初始化对象public int[] reset() { }        // 重置数组并返回public int[] shuffle() { }      // 随机打乱数组并返回
}

二、示例

输入:

["Solution", "shuffle", "reset", "shuffle"]
[[[1,2,3]], [], [], []]

输出:

[null, [3,1,2], [1,2,3], [1,3,2]]

解释:

Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 返回任意 [1,2,3] 的随机排列
solution.reset();   // 返回初始状态 [1, 2, 3]
solution.shuffle(); // 再次打乱

三、题目要求分析

我们需要支持三种操作:

  1. 初始化(Solution)
    保存原始数组,以便后续可以重置。

  2. reset()
    返回数组的初始状态。

  3. shuffle()
    返回数组的随机排列,并保证所有排列的概率相同。


四、核心思路:Fisher–Yates 洗牌算法

Fisher–Yates 是一种经典的等概率随机打乱算法
它保证每个元素在任意位置的概率完全相等。

算法过程:

设数组长度为 n

  1. 从后向前遍历数组;
  2. 对于每个位置 i,在 [0, i] 范围内随机选择一个索引 j
  3. 交换 nums[i]nums[j]

伪代码如下:

for i from n-1 to 1:j = random(0, i)swap(nums[i], nums[j])

这能确保:

  • 每个元素有 1/n 的概率出现在每个位置;
  • 所有 n! 种排列出现概率相等。

五、代码实现(Java)

import java.util.Random;class Solution {private int[] original;  // 保存初始数组private int[] array;     // 当前数组private Random rand;     // 随机数生成器// 构造函数:初始化数组public Solution(int[] nums) {original = nums.clone();array = nums.clone();rand = new Random();}// 重置数组到原始状态public int[] reset() {array = original.clone();return array;}// 打乱数组public int[] shuffle() {for (int i = array.length - 1; i > 0; i--) {int j = rand.nextInt(i + 1);  // 生成 [0, i] 范围内的随机索引swap(array, i, j);}return array;}// 交换两个元素private void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

在这里插入图片描述


六、执行流程示例

nums = [1, 2, 3] 为例:

  1. 初始化:

    original = [1, 2, 3]
    array = [1, 2, 3]
    
  2. 打乱过程:

    • i = 2, j = random(0, 2) → 假设 j = 0 → [3, 2, 1]
    • i = 1, j = random(0, 1) → 假设 j = 1 → [3, 2, 1]
      → 打乱结果可能为 [3,2,1] 或其他排列。
  3. reset:

    返回 [1, 2, 3]
    

七、复杂度分析

操作时间复杂度空间复杂度
reset()O(n)O(n)
shuffle()O(n)O(1)(原地打乱)
构造函数O(n)O(n)

八、为什么要使用 Fisher–Yates 算法?

许多初学者会尝试使用以下错误做法:

Collections.shuffle(Arrays.asList(nums));

这虽然能打乱数组,但并不适用于原生 int[],而且在算法面试中,你需要展示算法设计能力,而不仅是调用现成 API。

Fisher–Yates 洗牌算法是:

  • 数学上证明等概率
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)
  • 面试官非常喜欢考察的随机算法经典题。

九、进阶思考

  1. 如果数组中存在重复元素,如何保证“等概率”?

    • Fisher–Yates 仍然适用,只是最终排列会有重复结果。
  2. 如果想要每次 shuffle 结果都不重复,该如何实现?

    • 可以用一个集合记录已出现过的排列,但复杂度会非常高(O(n!)),通常不可行。
  3. 若需要部分打乱前 k 个元素,可以在洗牌时只迭代到 i = k - 1


十、总结

要点内容
核心算法Fisher–Yates 洗牌
实现要点从后向前遍历,每次随机交换
保证等概率每个元素独立均匀地分布在任意位置
时间复杂度O(n)
空间复杂度O(1)

十一、参考链接

  • LeetCode 384. Shuffle an Array
  • Fisher–Yates Shuffle(Wikipedia)

http://www.dtcms.com/a/532188.html

相关文章:

  • 芋道源码:VUE3部署:避坑--验证码不现显示,管理后台无法访问后端接口等,完善中。。。
  • 前端速通—ajax篇
  • 济南建立网站湖南建筑工程信息平台
  • android 堆栈打印
  • 太原网站优化排名北京互联网排名
  • 2025-web集群-问题总结
  • CATWIFI
  • 智能旅行助手Agent实战:前后端分离的多Agent系统
  • java基础-练习
  • nginx 配置超时时间
  • apache 配置超时时间
  • 网站开发工作时间个人网页设计作品及设计理念
  • 【Android】View 事件分发机制与源码解析
  • AIGC(生成式AI)试用 38 -- 程序(Python + OCR)-1
  • s001网站建设设计微信营销网络营销方式
  • #PCIE#《PCIE P2P 传输那点事儿》
  • HTTP | 跨域 - 知识点总结
  • 解决[PM2][ERROR] Script not found: D:\projects\xxx\start
  • 开发一款连接带有GEM/SECS协议软件的设备(一)
  • 大连微信网站开发app软件开发培训班
  • 同仁微网站建设工作室微信辅助网站制作
  • FFmpeg 基本数据结构 AVPacket分析
  • Linux at命令详解:轻松调度延迟任务
  • 线程停止、休眠、礼让、强制执行、观测线程状态
  • 复盘|嵌入式Linux驱动开发之I2C子系统
  • AI Agent常用的RAG有哪些种,分别适用于什么情况
  • 对中兴光猫zteOnu.exe项目的简单分析(提供下载地址)
  • 有没有专门做中考卷子的网站网站建设培训要多久
  • 做网站图片用什么格式最好个人网站备案能做宣传用么
  • JAVA1026 方法;类:抽象类、抽象类继承;接口、接口继承 Linux:Mysql