这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。
平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态、习惯、思路清晰程度等。
注意是简单实现,不是完整实现,重要的是概念清晰和实现思路清晰,建议
先解释清除概念=>写用例=>写伪代码=>再实现具体功能,再优化,一步步来。
25. 随机打乱数组
问题是什么
问题很清晰,就是给你个数组,让你随机打乱顺序。
分析
有一个常用写法是这个, 利用 Math.random 和 Array.prototype.sort()
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.sort(() => Math.random() - 0.5);
console.log(arr)
主要是这两个 api 的使用
Math.random
Math.random()函数返回一个浮点数,伪随机数在范围从0到小于1,也就是说,从0(包括0)往上,但是不包括1(排除1),然后您可以缩放到所需的范围。实现将初始种子选择到随机数生成算法; 它不能被用户选择或重置。
Math.random() 不能提供像密码一样安全的随机数字。不要使用它们来处理有关安全的事情。使用Web Crypto API 来代替, 和更精确的 window.crypto.getRandomValues() 方法.
Array.prototype.sort()
sort() 方法用
原地算法对数组的元素进行排序,并返回数组。(原数组会改变)默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
- 语法
arr.sort([compareFunction])
- 参数
compareFunction 可选
- 用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。
- firstEl
- 第一个用于比较的元素。
- secondEl
- 第二个用于比较的元素。
- 返回值
排序后的数组。请注意,数组已原地排序,并且不进行复制。
- 描述
如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
- 如果
compareFunction(a, b)小于0,那么 a 会被排列到 b 之前; - 如果
compareFunction(a, b)等于0, a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本); 如果 compareFunction(a, b)大于0, b 会被排列到 a 之前。compareFunction(a, b)必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。
所以我们用Math.random() - 0.5 返回一个正负未知的数,从而实现乱序。
此方法的局限性
且经过实验验证,用此方法打乱并不是概率相同的随机,会发现每个元素仍然有很大机率在它原来的位置附近出现。这样在做某些抽奖等现实需求会出问题。
而且由于 v8 出于对性能的考虑 在处理 sort 方法时,使用了插入排序和快排两种方案。当目标数组长度小于10时,使用插入排序;否则,使用快排。
这样会造成 10 这个分界线,两边概率分布又有所不同。
那么下面来介绍更好的方式
Fisher–Yates shuffle (洗牌算法)
这个算法是由 Ronald Fisher 和 Frank Yates 首次提出的。
总的来说思路是这样:
指针 i 从最后位置开始,从头开始到当前指针 i 位置中随机选出一个位置 j,i 和 j 位置替换,然后 i 指针前移直到头。如图
- 指针从尾部开始,因为index 从 0 开始,所以
i 先 -1, j 从 [0-4] 5 个数的位置中随机生成,跟 i 指针 (index=4)交换
[1, 2, 3, 4, 5]
|
i
- 比如随机生成的
j = 1
[1, 2, 3, 4, 5]
| |
j i
- 交换 可以用解构赋值, 注意是
值交换指针不动
[1, 5, 3, 4, 2]
| |
j i
- 下一轮循环 i--
[1, 5, 3, 4, 2]
| |
j i
- j 从剩下的 [0-3]中随机 ,比如
j = 2
[1, 5, 3, 4, 2]
| |
j i
- 第二轮交换
[1, 5, 4, 3, 2]
| |
j i
-
i 再前移
-
......
-
直到
i === 0
手写实现
function shuffle(arr) {
let i = arr.length;
while (i) {
let j = Math.floor(Math.random() * i--);
[arr[j], arr[i]] = [arr[i], arr[j]];
}
}
let arr = [1,2,3,4,5,6,7,8,9,10]
shuffle(arr)
console.log(arr)
现实使用
当然这种算法我们开发当然也可以直接使用各种库, 当然引入方式可以更优雅和更节省体积
推荐 babel-plugin-lodash 这个工具
import { shuffle } from 'lodash'
shuffle([1, 2, 3, 4])
// => [4, 1, 3, 2]
另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我
presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧