js sort排序_Javascript中 Array.sort 原理学习

介绍

sort()是JS中用来排序的一个方法,在我们学习各种经典排序算法的时候, 发现都“白学”了。其实在代码中一个sort()就可以解决,并且时间复杂度和空间复杂度相对都不会过高。其实sort()不光可以对数组进行排序, 基本数据类型的数组都可以, 并且可以实现对对象数组的排序。

这个sort其实远比我们想象的更加智能,它会基于要排序的数据量选择性能更加优秀的排序算法来实现排序。

原理

为什么sort()都可以排序呢? 而且时间和空间复杂度都还比较优良, 有没有人想过sort()里面是怎么实现的呢?

sort根据数组的长度进行区分的

当你的数组长度是小于一个定值的, 它会直接进行一个插入排序。

当长度大于定值时,有可能会merge排序或者是quick排序, merge和quick会将整个数组进行划分,进行递归,一旦划分的子数组长度小于定值时, 将不再递归划分,直接进行插入排序。为什么会这么排序呢?

原因是,在数据量小的时候,插入排序的常数代价非常低,虽然时间复杂度是O(n平方),但是常数项比较低,此时, 在数据量比较小的时候,优势就展现出来了。

这个定值是多少?有些人说是60,但是我觉得值得去研究。

数据量大的时候用merge或quick排序

那么问题又来而来,当数据量比较大需要进行划分的时候,什么时候用merge,什么时候用quick呢?

这就要取决数组的类型了,当是基本数据类型的时候,会用quick, 当是对象类型的时候会用merge。

因为当是基本数据类型的时候不需要考虑稳定性的因素,但是对象类型就要考虑了, 因为对象不止用来排序的这一个属性, 可能已经在其他属性已经排好序了, 在此番排序之后还需要保证数组的稳定性。

排序算法对比图标

9e9dcf086ad1df48c43f6f7cc93f0546.png

d1842248f7c5d128b58ead5102432778.png

插入排序思路

d40bd1e66f6aafdcebe8550085181da0.png

代码实现

function swap(arr, i, j){    let temp = arr[i];    arr[i] = arr[j];    arr[j] = temp;}function insertSort(arr){    if(!arr || arr.length <=1) {        return;    }    for(let i=1; i< arr.length;i++) {        for(let j=i-1; j >= 0; j--) {            if(arr[j] > arr[j+1]) {                swap(arr, j, j+1);            }            else {                break;            }        }    }}// let arr = [2,5,3,1,4];let arr = [8, 3, 4, 5, 11, 6, 7, 9, 10];insertSort(arr);console.log(arr);

快速排序思路

快速排序是对冒泡排序的一种改进,采用递归分治的方法进行求解。而快排相比冒泡是一种不稳定排序,时间复杂度最坏是O(n^2),平均时间复杂度为O(nlogn),最好情况的时间复杂度为O(nlogn)。

对于快排来说,「基本思想」是这样的

  • 快排需要将序列变成两个部分,就是「序列左边全部小于一个数」「序列右面全部大于一个数」,然后利用递归的思想再将左序列当成一个完整的序列再进行排序,同样把序列的右侧也当成一个完整的序列进行排序。

  • 其中这个数在这个序列中是可以随机取的,可以取最左边,可以取最右边,当然也可以取随机数。但是「通常」不优化情况我们取最左边的那个数。

a4b52cb45f8a2341fee0565a75470e38.png

代码实现

function swap(arr, i, j){    let temp = arr[i];    arr[i] = arr[j];    arr[j] = temp;}function quickSort(arr, from, to) {    // 递归出口    if(from > to) {        return;    }    // 设置基准元素    let flag = arr[from];    // 设置两个哨兵    let left = from;    let right = to;    while(left < right) {        // 1.必须先移动右侧哨兵        while(left < right && arr[right] <= flag){            right--;        }        // 2. 移动左侧哨兵        while(left < right && arr[left] >= flag){            left++;        }        if(left < right) {            swap(arr, left, right);        }    }    arr[from] = arr[left];    arr[left] = flag;    // 递归    quickSort(arr, from, left-1);    quickSort(arr, left+1, to);}let arr = [8, 3, 4, 5, 11, 6, 7, 9, 10];quickSort(arr, 0, arr.length-1);console.log(arr);

归并排序原理

归并和快排都是「基于分治算法」的,分治算法其实应用挺多的,很多分治会用到递归,但事实上「分治和递归是两把事」。分治就是分而治之,可以采用递归实现,也可以自己遍历实现非递归方式。而归并排序就是先将问题分解成代价较小的子问题,子问题再采取代价较小的合并方式完成一个排序。

至于归并的思想是这样的:

  • 第一次:整串先进行划分成一个一个单独,第一次是将序列中(1 2 3 4 5 6---)两两归并成有序,归并完(xx xx xx xx----)这样局部有序的序列。

  • 第二次就是两两归并成若干四个(1 2 3 4 5 6 7 8 ----)「每个小局部是有序的」

  • 就这样一直到最后这个串串只剩一个,然而这个耗费的总次数logn。每次操作的时间复杂的又是O(n)。所以总共的时间复杂度为O(nlogn).

699ebfef9f206b7df7cd32cf92ed7583.png

ee3213be1d6f7fc922f86cae5e2afc2a.png

代码实现

private static void mergesort(int[] array, int left, int right) {  int mid=(left+right)/2;  if(left<right)  {    mergesort(array, left, mid);    mergesort(array, mid+1, right);    merge(array, left,mid, right);  }}private static void merge(int[] array, int l, int mid, int r) {  int lindex=l;int rindex=mid+1;  int team[]=new int[r-l+1];  int teamindex=0;    while (lindex<=mid&&rindex<=r) {//先左右比较合并    if(array[lindex]<=array[rindex])    {      team[teamindex++]=array[lindex++];    }    else {          team[teamindex++]=array[rindex++];    }  }  while(lindex<=mid)//当一个越界后剩余按序列添加即可  {    team[teamindex++]=array[lindex++];  }  while(rindex<=r)  {    team[teamindex++]=array[rindex++];  }   for(int i=0;i<teamindex;i++)  {    array[l+i]=team[i];  }}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值