javascript 常见排序算法

排序中的通用函数。

function checkArray(array) {
  if (!array || array.length < 2) return
}
function swap(array, left, right) {
  let rightValue = array[right]
  array[right] = array[left]
  array[left] = rightValue
}

冒泡排序:

原理如下:从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要在比较最后一个元素,只需要比较到length - 1 的位置。

    function bubble(array) {
      checkArray(array);
      
      let len = array.length;
      for(let i = len - 1; i > 0; i--){
        for(let j = 0; j < i; j++){
          if(array[j] > array[j+1]) {
            swap(array, j, j + 1);
          }
        }
      }
      return array;
    }

当数组正序时候,复杂度为O(n),当逆序的时候,复杂度:O(n*n);

时间复杂度:O(n*n),空间复杂度O(1)

插入排序:

原理如下:第一个元素默认是已排序元素,取出下一个元素和当前元素比较,如果当前元素大就交换位置。那么此时第一个元素就是当前的最小数,所以下次去除操作从第三个元素开始,向前对比,重复之前的操作。

    function insertion(array) {
      checkArray(array);

      let len = array.length;
      for(let i = 1; i < len; i++) {
        for(let j = i - 1; j >= 0 && array[j] > array[j+1]; j--){
          swap(array, j, j+1);
        }
      }
      return array;
    }

时间复杂度:O(n*n),空间复杂度O(1)

选择排序: 

原理如下:遍历数组,设置最小值的索引为0,如果取出的值比当前最小值小,就替换最小值索引,遍历完成后,将第一个元素和最小值索引上的值交换。如上操作后,第一个元素就是数组中的最小值,下次遍历的就可以从索引1开始重复上述操作。

    function selection(array) {
      checkArray(array);

      let len = array.length;
      for(let i = 0; i < len - 1; i++) {
        let minIndex = i;
        for(let j = i + 1; j < len; j++) {
          minIndex = array[j] < array[minIndex] ? j : minIndex;
        }
        swap(array, i, minIndex);
      }
      return array;
    }

时间复杂度:O(n*n),空间复杂度O(1);

归并排序:

原理如下:递归的将数组两两分开直到最多包含两个元素,然后将数组排序合并,最终合并为排序好的数组。假设我有一组数组【3,1,2,8,9,7,6】中间索引是3,先排序数组【3,1,2,8】。在这个左边数组上,继续拆分直到变成数组包含两个元素(如果数组是奇数的话,会有一个拆分数组只包含一个元素)。然后排序数组【3,1】和【2,8】,然后再排序数组【1,3,2,8】,这样左边数组就排序完成,然后按照以上思路排序右边数组,最后将数组【1,2,3,8】和【6,7,9】排序。

    function mergeSort(array, left, right) {
      if(left === right) {
        return;
      }
      let mid = parseInt(left + ((right - left) >> 1))
      mergeSort(array, left, mid);
      mergeSort(array, mid + 1, right)
      let help = [];
      let i = 0;
      let p1 = left;
      let p2 = mid + 1;
      while(p1 <= mid && p2 <= right) {
        help[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++]
      }
      while(p1 <= mid) {
        help[i++] = array[p1++];
      }
      while(p2 <= right) {
        help[i++] = array[p2++];
      }
      let len = help.length;
      for(let i = 0; i < len; i++) {
        array[left + i] = help[i];
      }
      return array;
    }
    function sort(array) {
      checkArray(array);
      mergeSort(array, 0, array.length - 1);
      return array;
    }
    sort([3,1,2,8,9,7,6])

以上算法使用了递归的思想,递归的本质就是压栈,每递归执行一次函数,就将该函数的信息,比如(参数,内部的变量,执行到的行数)压栈,直到遇到终止条件,然后出站并继续执行函数。对于以上递归函数的调用轨迹如下:

mergeSort(data, 0, 6) // mid = 3
mergeSort(data, 0, 3) // mid = 1
mergeSort(data, 0, 1) // mid = 0
mergeSort(data, 0, 0) // 遇到终止,回退到上一步
mergeSort(data, 1, 1) // 遇到终止,回退到上一步
// 排序 p1 = 0, p2 = mid + 1 = 1
// 回退到 `mergeSort(data, 0, 3)` 执行下一个递归
mergeSort(2, 3) // mid = 2
mergeSort(2, 2) // 遇到终止,回退到上一步
mergeSort(3, 3) // 遇到终止,回退到上一步
// 排序 p1 = 2, p2 = mid + 1 = 3
// 回退到 `mergeSort(data, 0, 3)` 执行合并逻辑
// 排序 p1 = 0, p2 = mid + 1 = 2
// 执行完毕回退
// 左边数组排序完毕,右边也是如上轨迹

 时间复杂度:O(n*logn),空间复杂度O(1);

快速排序: 

原理如下:随机选取一个数组中的值作为基准值,从左至右取值与基准值对比大小。比基准值小的放数组左边,大的放右边,对比完成后将基准值和第一个比基准值大的值交换位置。然后将数组以基准值的位置分为两部分,继续递归以上操作。

时间复杂度:最坏情况,O(n*n),最好情况,O(n*logn),空间复杂度O(logn);

   //方法一:
 function quickSort(array) {
      if (array.length <= 1) { return array; }
      let pivotIndex = Math.floor(array.length/2);
      let pivot = array.splice(pivotIndex, 1)[0];
      let left = [], right = [], len = array.length;

      for(let i = 0; i < len; i++) {
        if(array[i] < pivot) {
          left.push(array[i])
        }
        else{
          right.push(array[i])
        }
      }
      return quickSort(left).concat([pivot], quickSort(right))
    }
//方法二:
    function quickSort(arr,low,high){
      var key=arr[low];
      var start=low;
      var end=high;
      while(end>start){
        while(end>start&&arr[end]>=key) end--;
        if(arr[end]<=key){
          var temp = arr[end];
          arr[end]=arr[start];
          arr[start] = temp;
        }
        while(end>start&&arr[start]<=key) start++;
        if(arr[start]>=key){
          var temp = arr[start];
          arr[start]=arr[end];
          arr[end]=temp;
        }
	  	}
      if(start>low) quickSort(arr,low,start-1);
      if(end<high) quickSort(arr,end+1,high);
      return arr;
    }
//方法三:
 function part(array, left, right) {
      if (arr.length <= 1) { return; }
      let less = left - 1;
      let more = right;
      while (left < more) {
        if (array[left] < array[right]) {
          // 当前值比基准值小,`less` 和 `left` 都加一
        ++less;
          ++left;
        } else if (array[left] > array[right]) {
          // 当前值比基准值大,将当前值和右边的值交换
          // 并且不改变 `left`,因为当前换过来的值还没有判断过大小
          swap(array, --more, left);
        } else {
          // 和基准值相同,只移动下标
          left++;
        }
      }
      // 将基准值和比基准值大的第一个值交换位置
      // 这样数组就变成 `[比基准值小, 基准值, 比基准值大]`
      swap(array, right, more);
      return more;
    }
    let arr = [23, 43, 43, 5, 2, 32, 42, 556, 27, 34];
    // console.log(part(arr, 0 ,9));
    function swap(array, left, right) {
      [array[left], array[right]] = [array[right], array[left]];
    }
    function quickSort(arr, left, right) {
      
      let random = left; 
      swap(arr, random, right);
      if(left<right) {
        let index = part(arr, left, right);
        if(index > left)
        quickSort(arr, left, index-1);
        if(index < right)
        quickSort(arr, index + 1, right);
      }
      return arr;
    }
//方法四:
function quickSort(arr, from,to) {
      let i = from, j = to, key = arr[from];
      if(from >= to) return;
      while(i<j) { 
        while(i<j && arr[j]>=key)
          j--;
        while(i<j && arr[i]<=key)
          i++;
       
        if(i<j) {
          swap(arr, i, j);
        }
      }
      arr[from] = arr[i]
      arr[i] = key;
      quickSort(arr, from, i-1);
      quickSort(arr, i+1, to);
      return arr;
    }

堆排序: 

原理如下:堆排序利用了二叉堆特性来做,二叉堆通常用数组表示,并且为一颗完全二叉树,二叉堆又分为大根堆和小根堆。

堆排序的原理就是组成一个大根堆或者小跟堆。一小跟堆为例,某个节点的左边子节点索引是i * 2 + 1,右边是i * 2 + 2, 父节点是(i - 1)>> 1。

  1. 首先遍历数组,判断该节点的父节点是否比他小,如果小就交换位置并继续判断,知道他的父节点比他大。
  2. 重复以上操作,直到数组首位是最大值。
  3. 然后将首位和末尾交换位置并将数组长度减一,表示数组末尾已是最大值,不需要在比较大小。
  4. 对比左右节点那个大,然后记住大的节点的索引并且和父节点对比大小,如果子节点大舅交换位置。
  5. 重复以上操作3- 4 直到整个数组都是小根堆。如图
  //堆排序
  function heap(array) {
    checkArray(array);
    for(let i = 1; i < array.length; i++){
      heapInsert(array, i);
    }
    let size = array.length;
    swap(array, 0, --size);
    while(size > 0){
      heapify(array, 0, size)
      swap(array, 0, --size)
    }
    return array;
  }
  //当前节点大于父节点就交换
  function heapInsert(array, index){
    while(array[index] > array[(index - 1) >> 1]) {
      swap(array, index, (index - 1) >> 1)
      index = (index - 1) >> 1;
    }
  }
  //
  function heapify(array, index, size) {
    let left = index * 2 + 1;
    while(left < size) {
      let largest = left + 1 < size && array[left] < array[left + 1] ? left + 1 : left;
      largest = array[index] < array[largest] ? largest : index;
      if(largest === index) break
      swap(array, index, largest);
      index = largest;
      left = index * 2 + 1;
    }
  }

时间复杂度:O(n*logn),空间复杂度O(logn);

系统自带排序:

每个语言的排序内部实现都是不同的。

对于JS来说,数组长度大于10会采用快排,否则使用插入排序。源码实现

选择插入排序是因为虽然时间复杂度很差,但是在数据量很小的情况下和O(N*logN)相差无几,然而插入排序需要的常数时间很小,所以相对别的排序来说更快。

对于JAVA来说,还会考虑内部元素的类型。对于存储对象的数组来说,会采用稳定性好的算法。稳定性的意思就是对于想同志来说,相对顺序不能改变。

时间复杂度:O(n*logn),空间复杂度O(1);

希尔排序(插入排序的一种):

原理如下:希尔排序(Shell's Sort)在插入排序算法的基础上进行了改进,算法的时间复杂度与前面几种算法相比有较大的改进。其算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。

//https://blog.csdn.net/qq_39207948/article/details/80006224
//https://blog.csdn.net/lhjuejiang/article/details/80505127
function shellSort(arr) {
    let len = arr.length,
        temp,
        gap = 1;
    while(gap<len/3){
      gap = gap * 3 + 1;
    }
    let i, j;
    for(gap; gap > 0; gap = Math.floor(gap/3)){
      for(i = gap; i < len; i++){
        temp = arr[i];
        for(j = i - gap; j >= 0 && arr[j] > temp; j-=gap){
          arr[j+gap] = arr[j];
        }
        arr[j+gap] = temp;
      }
    }
    return arr;
  }

复杂度:

希尔排序的复杂度和增量序列是相关的

{1,2,4,8,...}这种序列并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是O(n^2)

Hibbard提出了另一个增量序列{1,3,7,...,2^k-1},这种序列的时间复杂度(最坏情形)为O(n^1.5)

Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,...}

来自:https://yuchengkai.cn/docs/cs/algorithm.html#%E6%8E%92%E5%BA%8F

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值