前端排序算法

Posted gil-z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端排序算法相关的知识,希望对你有一定的参考价值。

一.冒泡排序

原理:简单来说就是相邻两个元素进行对比,按照你需要的排序方式(升序or降序)进行位置替换,替换时需要额外一个变量当作中间变量去暂存值。

总结步骤:

     1、外循环是遍历每个元素,每次都放置好一个元素;   

     2、内循环是比较相邻的两个元素,把大/小的元素交换到后面;

     3、等到第一步中循环好了以后也就说明全部元素排序好

function Bubble(arr) {
      for (let i = 0; i < arr.length; i++) {//遍历数组每一个(回合数)
        for (let j = 0; j < arr.length - 1 - i; j++) {//真正进行比较,例:i=0时比较第一次,进入第二层循环arr[0]与arr[1]进行比较,符合交换条件即交换,不符合继续比较
          if (arr[j] < arr[j + 1]) {
            let temp = arr[j + 1]//交换变量
            arr[j + 1] = arr[j]
            arr[j] = temp
          }
        }
      }
      return arr
}

时间复杂度:http://blog.csdn.net/itachi85/article/details/54882603

  最好的情况:数组已经排好序(不用交换元素)----->  时间花销为:[ n(n-1) ] /  2;所以最优的情况时间复杂度为:O( n^2 )

  最差的情况:也就是开始的时候元素是逆序的,那么每一次排序都要交换两个元素,则时间花销为:[ 3n(n-1) ] / 2;(其中比上面最优的情况所花的时间就是在于交换元素的三个步骤);所以最差的情况下时间复杂度为:O( n^2 );

综上:

  最优的时间复杂度为:O( n^2 ) ;//当使用标志位方法来判断你是否排好序时,时间复杂度为O(n)

       最差的时间复杂度为:O( n^2 );

       平均的时间复杂度为:O( n^2 );

加上flag的代码如下:

function bubbleSort2(arr) {var i = arr.length-1;  //初始时,最后位置保持不变
    while ( i> 0) {
        var flag= 0; //每趟开始时,无记录交换
        for (var j= 0; j< i; j++)
            if (arr[j]> arr[j+1]) {
                flag= j; //记录交换的位置,flag之后的均已交换成功
                var tmp = arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=tmp; } i= flag; //为下一趟排序作准备,只要扫描到flag位置即可 }return arr; }

 

二:选择排序

原理:首先从原始数组中找到最小的元素,并把该元素放在数组的最前面,然后再从剩下的元素中寻找最小的元素,放在之前最小元素的后面,直到排序完毕

function Choose(arr) {
      let maxIndex, temp;
      for (let i = 0; i < arr.length - 1; i++) {
        maxIndex = i//maxIndex始终作为最大值的位置索引,
        for (let j = i + 1; j < arr.length; j++) {//当前最大值的后一位开始比较
          if (arr[j] > arr[maxIndex]) {//当后一位大于当前maxIndex
            maxIndex = j//将最大位置的索引值变为两者中较大的
          }
        }
        temp = arr[i]//当前轮次中的i与最大值进行交换,以达成最大值在前的的目的
        arr[i] = arr[maxIndex]
        arr[maxIndex] = temp
      }
      return arr
    }

时间复杂度:

       平均时间复杂度:O(n*n)

  最好情况:O(n*n)

  最差情况:O(n*n)

三:插入排序

原理:一组数据分成两组,分别将其称为有序组与待插入组。每次从待插入组中取出一个元素,与有序组的元素进行比较,并找到合适的位置,将该元素插到有序组当中。就这样,每次插入一个元素,有序组增加,待插入组减少。直到待插入组元素个数为0。

 function InsertionSort(array) {
      var length = array.length;
      for (var i = 0; i < length - 1; i++) {//i代表已经排序好的序列最后一项下标(第一项已排好序)
        var insert = array[i + 1];//insert为待插入组的第一项
        var index = i + 1;//记录要被插入的下标
        for (var j = i; j >= 0; j--) {
          if (insert > array[j]) //要插入的项比它小/大,往后移动
            array[j + 1] = array[j];
            index = j;
          }
        }
        array[index] = insert;
      }
      return array;
    }

时间复杂度:

       平均时间复杂度:O(n*n)

  最好情况:O(n)

  最差情况:O(n*n)

 

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

原理:利用步长来进行两两元素比较,然后缩减步长在进行排序。

  复制了一个详细的说明:希尔排序的实质是分组插入排序,该方法又称缩小增量排序。该方法的基本思想是:先将整个待排元素序列分割为若干个子序列(由相隔某个‘增量’的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,带这个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况)效率是很高的,因此希尔排序在时间效率上有较大的提高。 
希尔排序算法实现。

与插入排序的不同之处:它会优先比较距离较远的元素

function shellSort(arr) {
      let temp,
        gap = 1;
      while (gap < arr.length / 3) {
        gap = gap * 3 + 1//动态定义间隔序列
      }
      for (gap; gap > 0; gap = Math.floor(gap / 3)) {//控制步长(间隔)并不断缩小
        for (var i = gap; i < arr.length; i++) {//按照增量个数对序列进行排序
          temp = arr[i]
          for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {//例:j=0  arr[1]>arr[5]
            arr[j + gap] = arr[j]
          }
          arr[j + gap] = temp
        }
      }
      return arr
    }

 

时间复杂度:

  最佳情况: O(nlog2 n)

  最坏情况: O(nlog2 n)

  平均情况: O(nlog n)

五:归并排序

原理:采用分治法,先划分,再合并

 步骤:

     1.先将C划分为两个数组A和B(即把数组C从中间分开)
  2.再分别对数组A、B重复步骤1的操作,逐步划分,直到不能再划分为止(每个子数组只剩下一个元素),这样,划分的过程就结束了。
  如:              [12 20 30 21 15 33 26 19 40 25]
  划分为:  [12 20 30 21 15]                [33 26 19 40 25]
           [12 20]      [30 21 15]       [33 26]       [19 40 25]
         [12]  [20]   [30]  [21 15]     [33]  [26]    [19]    [40 25]
         [12]  [20]   [30] [21] [15]    [33]  [26]    [19]   [40] [25]
  3.然后从下层往上层不断合并数组,每一层合并相邻的两个子数组,合并的过程是每次从待合并的两个子数组中选取一个最小的元素,然后把这个元素放到合并后的数组中,不断重复直到把两个子数组的元素都放到合并后的数组为止。

  4.依次类推,直到合并到最上层结束,这时数据的排序已经完成了。

 function mergeSort(arr) {
      if (arr.length < 2) {
        return arr;
      }
      //将数组分为左右两个子序列
      let middle = Math.floor(arr.length / 2)
      let left = arr.slice(0, middle)
      let right = arr.slice(middle)
      return merge(mergeSort(left), mergeSort(right))//对两个子序列重复划分为两个进行排序,直到不能划分
    }
    function merge(left, right) {
      let result = []
      while (left.length > 0 && right.length > 0) {
        if (left[0] < right[0]) {//两两比较,较小的放入结果数组
          result.push(left.shift())//shift()方法删除数组第一个元素,并返回被删除的元素
        } else {
          result.push(right.shift())
        }
      }
      /* 当左右数组长度不等.将比较完后剩下的数组项链接起来即可 */  
        return result.concat(left).concat(right);  
    }

时间复杂度:

  最佳情况:T(n) = O(n)

  最差情况:T(n) = O(nlogn)

  平均情况:T(n) = O(nlogn)

六:快速排序

原理:选择一个基准,将比基准小的放左边,比基准小的放在右边(基准处在中间位置)

    function quickSort2(arr) {
      //如果数组<=1,则直接返回
      if (arr.length <= 1) { return arr; }
      var pivotIndex = Math.floor(arr.length / 2);
      //找基准,并把基准从原数组删除
      var pivot = arr.splice(pivotIndex, 1) [0];
      //定义左右数组
      var left = [];
      var right = [];

      //比基准小的放在left,比基准大的放在right
      for (var i = 0; i < arr.length; i++) {
        if (arr[i] <= pivot) {
          left.push(arr[i]);
        }
        else {
          right.push(arr[i]);
        }
      }
      //递归
      return quickSort2(left).concat([pivot], quickSort2(right));
    }

时间复杂度:

  最佳情况:T(n) = O(nlogn)

  最差情况:T(n) = O(n2)

  平均情况:T(n) = O(nlogn)

排序算法所有时间复杂度及空间复杂度,稳定性等:

技术分享图片

 

掘金的这个文章写的很详细:https://juejin.im/post/57dcd394a22b9d00610c5ec8

 











以上是关于前端排序算法的主要内容,如果未能解决你的问题,请参考以下文章

7种基本排序算法的Java实现

算法排序之堆排序

前端排序算法总结;前端面试题2.0;JavaScript异步编程

快速排序-递归实现

JavaScript 数据结构与算法之美 - 归并排序快速排序希尔排序堆排序

前端常用算法笔记-插入排序