情景
今天刷 LeetCode 的时候,有一道题是需要先给数据排序再进行运算,因为重点不是排序所以我直接使用了 JS 中的 Array.prototype.sort() 方法,结果答案一直不正确,我以为是主要算法出问题了,然后看了下和正确答案是一样的,就找不到是什么原因。
然后我把代码搬到 VS Code 里头调试,发现数组并没有按照数字大小排序,代码如下:
const arr = [1, 100, 21, 23, 3]
arr.sort()
console.log(arr) // [ 1, 100, 21, 23, 3 ]
解决
我到 MDN 上查询相关文档:Array.prototype.sort() ,发现里面有一句话是这么写的:
sort()
方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的。
重点如同加粗部分,他竟然将数值全部转换为了字符串再根据 UTF-16 值来比较的,我以前竟然一直不知道。
字符串的排序其实很常见,比如在 Windows 中,你把文件按名称进行排列,就是比较字符串的 UTF-16(也有可能是 UTF-8,不过 Windows 中的数字排序还是按照数值本身排的,估计是考虑到正常人类思维进行的改动)。
具体的排序原理可以参照:算法-22-字符串的排序算法(四种排序)。
因此,在 JS 中,如果想将数字数组进行排序,正确方法是还需传入一个比较函数:
array.sort((a, b) => a - b);
这个比较函数,会传入两个参数,为当前正在进行比较的两个值,如上面的代码。如果 a 比 b 小,应该返回负数;a 比 b 大,返回正数;否则返回 0。a - b
是一种简写方法,你也可以改成 if 语句。
array.sort((a, b) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
传入一个自定义比较函数有一个好处,就是可以不再将排序的数据限制在数字或者字符串,可以是一个对象,比如:
const array = [
{
id: 0,
name: 'Kafuu Chino',
isWife: true
},
{
id: 1,
name: 'Miyohashi Kori',
isWife: false
}
];
array.sort((a, b) => a.id - b.id); // 这样可以根据数组元素对象中的 id 字段进行顺序排序。
另外,如果你想倒序排序,那么 a - b
改成 b - a
即可。