【JavaScript笔记 · 特殊篇】Array.sort()方法在Google V8 6.0.0环境下存在的同值乱序BUG的详细解决方法

首先这个问题是我在牛客网练算法题(具体题号:No.HJ26)的时候遇见的,也是困扰了我好久才解决,所以我打算写成博客的形式,给遇到类似问题的童鞋速度解决困扰。

一. 问题阐述

算法中需要对字符串里的英文字母不分大小写按字典顺序重新排列,特殊字符原位输出,例子如下:

  • Type ——> epTy
  • BabA ——> aABb
  • By?e ——> Be?y

使用字符串A Famous Saying: Much Ado About Nothing (2012/8).分别的输出情况:

  1. 在vscode里的Node.js环境下运行结果为:

在这里插入图片描述

  1. 之后粘到牛客网上的编辑器窗口里运行(Google V8 6.0.0环境)。显示没通过所有的测试用例,细看那个未通过的用例,我突然发现怎么输出结果和在vscode里不一样???

在这里插入图片描述
仔细比对后发现原来是字母相同的部分排列顺序有出入…啊…这…(ˉ▽ˉ;)…

二. 结论👇

Array.sort()排序在v8引擎中可能会存在同值乱序的BUG:
具体的原因是因为Google v8引擎为了排序的高效性,针对不同数组长度采取了不同的排序策略:

数组长度在10以下用的是快速排序算法,和其他浏览器环境或者Node.js环境下的运行结果相同;超过10之后Google v8引擎就会调用另一种排序方法——插入排序(又称为不稳定排序) ,这就是同值乱序BUG的根源所在。

我们来验证一下:
先输入一共10个字符(8个大小写不同的A和两个空格字符):按以上结论应该会原样输出,通过测试。
在这里插入图片描述
果然是这样,然后追加一个 b,这样数组长度就超过10了,运行发现果然之前的A都乱序了。
在这里插入图片描述

三. 解决方案

摘出之前的sort排序代码块,字母相同情况下返回 0 ,即为不排序,按正常思维不都是值相等默认按原顺序排列嘛,这不就乱序了捏…

arr.sort(function(a, b){ // 不分大小写的字典顺序排列
    var x = a.toLowerCase(), y = b.toLowerCase();
    return x < y ? -1 : x > y ? 1 : 0;
});

现在可以按如下方案修改代码:
在分离字符的时候也保存每个字符在原数组中的位置下标。在字符值相同的情况下,就可以比较两者原数组下标来排序。

 for(var i=0;i<arr.length;i++){
        if(arr[i] < 'A' || (arr[i] > 'Z' && arr[i] < 'a') || arr[i] > 'z'){
            arr1.push([i,arr[i]]);
        }else{
            arr2.push([i,arr[i]]);
        };
    };
    arr2.sort(function(a, b){
        var x = a[1].toLowerCase(), y = b[1].toLowerCase();
        return x < y ? -1 : x > y ? 1 : a[0] - b[0];
    });

刚才出错的用例也顺利通过了!!!
在这里插入图片描述


2020.8.11 更新

四. 问题现状(内核7.0.0以上Google v8引擎已不存在)

首先说明内核版本7.0.0以上的Google v8引擎中早已经升级了Timsort排序算法,这个问题早就不存在了。地址栏输入chrome://version/可查看自己的内核版本:
在这里插入图片描述
不得不说牛客网上的6.0.0的Google V8引擎是真的坑…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值