洗牌算法

335 阅读1分钟

在上周三早上面试的时候,一道题目是将 let arr = [1,2,3,4,5] 的数组处理成乱序,当时没有写出来,想着应该短期内不会遇到这题,就木有将这道题复盘,可巧合的是,周五早上的面试,又是同样的题目,呃呃呃,嘤嘤嘤......

目前找到了两种思路,这两种思路都需要一个在区间 [0, arr.length] 的区间中取一个随机值,辅助函数代码如下:

function getRandom(min, max) {
    let random = Math.floor(Math.random() * (max - min + 1) + min)
    return random
}

1.换牌

假设数组长度为 n = arr.length,将数字当成是牌

  • 先将第1张牌和第1张牌更换,即,位置不变
  • 再将第2张牌和前2张牌中的任意1张更换
  • 再将第k张牌和前k张牌中的任意1张更换
  • 将第n-1张牌和前n-l张牌中的任意1张更换
  • 将第n张牌和任意1张牌更换
for (let i = 0; i < arr.length; i++) {
    let j = getRandom(0, i);
    let t = arr[i]
    arr[i] = arr[j]
    arr[j] = t
}

换牌的另一种方式是,将第1张和任意1张进行更换,将第2张和任意1张进行更换,依次类推,将最后一张牌和任意1张进行更换

for (let i = 0; i < arr.length; i++) {
    let t = arr[i]
    let j = getRandom(0, arr.length);
    arr[i] = arr[j]
    arr[j] = t
}

2.抽牌

假设数组长度为 n = arr.length,将数字当成是牌

  • 先随机在n张牌中抽取1张放到新的牌堆上
  • 在剩余的n-1张牌中抽取1张放到新的牌堆上
  • 在剩余的k张牌中抽取1张放到新的牌堆上
  • 在剩余的2张牌中抽取1张放到新的牌堆上
  • 将最后1张牌放到新的牌堆上
let newArr = []
let len = arr.length
for (let i = 0; i < len; i++) {
    let index = getRandom(0, arr.length - 1);
    newArr.push(arr[index])
    arr.splice(index, 1)
}

抽牌的另一种方式是,不需要开辟新的牌堆,直接将抽到的牌依次放到牌底即可,在真实代码中,可减少新数组的创建,减少空间复杂度

for (let i = 0; i < arr.length; i++) {
    let index = getRandom(0, len - 1);
    let buff = arr.splice(index, 1)
    arr.push(...buff)
    len--
}

以上是分别使用换牌和抽牌的方式进行的分析。

声明:换牌思路参考黄轶老师《全网稀缺Vue 2.0高级实战 独立开发专属音乐WebAPP》播放器随机模式的算法
链接为coding.imooc.com/class/107.h…