常见算法效率比较:

js 排序方法 - 图1

一. 冒泡排序
冒泡排序是是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把它们交换过来。遍历数列的工作是重复的进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端
1.冒泡排序算法的运作如下:
(1)比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个
(2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素还是最大的数
(3)针对所有的元素重复以上的步骤,除了最后一个
2.冒泡排序的分析:**
交换过程图示(第一次)

js 排序方法 - 图2
那么我们需要进行n-1次冒泡过程,每次对应的比较次数如下图所示
js 排序方法 - 图3
代码如下:

  1. function bubbleSort(arr) {
  2. var len = arr.length;
  3. for (var i = 0; i < len - 1; i++) {
  4. for (var j = 0; j < len - 1 - i; j++) {
  5. if (arr[j] > arr[j+1]) { // 相邻元素两两对比
  6. var temp = arr[j+1]; // 元素交换
  7. arr[j+1] = arr[j];
  8. arr[j] = temp;
  9. }
  10. }
  11. }
  12. return arr;
  13. }

3. 时间复杂度
算法的时间复杂度是指算法执行的过程中所需要的基本运算次数
(1)最优时间复杂度:O(n)(表示遍历一次发现没有任何可以交换的元素,排序结束)
(2)最坏时间复杂度:O(n)
(3)稳定性:稳定
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的
常见算法的稳定性(要记住)
堆排序快速排序希尔排序直接选择排序不是稳定的排序算法,而基数排序冒泡排序直接插入排序折半插入排序归并排序是稳定的排序算法。
二. 选择排序
选择排序是一种简单直观的排序算法。他的工作原理如下:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置(末尾位置),然后,再从剩余未排序元素中继续寻找最小(大)元素然后放到已排序序列的末尾以此类推,直到所有元素均排序完毕
选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,他们当中至少有一个将被移到最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动 元素的排序方法中,选择排序属于非常好的一种

1. 选择排序分析

排序过程:
js 排序方法 - 图4js 排序方法 - 图5
红色表示当前最小值,黄色表示已排序列,蓝色表示当前位置

具体代码:

function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}
  1. 时间复杂度
    (1)最优时间复杂度:O(n)
    (2)最坏时间复杂度:O(n)
    (3)稳定性:不稳定(升序的时候不稳定,相等两个数的相对位置一定会发生变化)
    三. 插入排序

    插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在从后向前扫描的过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间

    1.插入排序分析:
    js 排序方法 - 图6js 排序方法 - 图7

代码实现:

function insertionSort(arr) {
    var len = arr.length;
    var preIndex, current;
    for (var i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while(preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr;
}


2. 时间复杂度
(1)最优时间复杂度:O(n)(升序排列,序列已经处于升序状态)
(2)最坏时间复杂度:O(n)
(3)稳定性:稳定
四. 快速排序
快速排序,又称划分交换排序。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都要小,然后再按此方法对两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
步骤为:
(1) 从数列中挑选一个元素,称为“基准”
(2) 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以放到任意一边,但一般都统一放到一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区操作
(3)递归的把小于基准值元素的子数列和大于基准值元素的子数列排序
递归的最底部情形,是数列的大小基准值元素的子数列和大于基准值元素的子数列排序

1.快速排序的分析:
js 排序方法 - 图8
代码如下:

function quickSort(arr, left, right) {
    var len = arr.length,
        partitionIndex,
        left = typeof left != 'number' ? 0 : left,
        right = typeof right != 'number' ? len - 1 : right;

    if (left < right) {
        partitionIndex = partition(arr, left, right);
        quickSort(arr, left, partitionIndex-1);
        quickSort(arr, partitionIndex+1, right);
    }
    return arr;
}

function partition(arr, left ,right) {     // 分区操作
    var pivot = left,                      // 设定基准值(pivot)
        index = pivot + 1;
    for (var i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }        
    }
    swap(arr, pivot, index - 1);
    return index-1;
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
function partition2(arr, low, high) {
  let pivot = arr[low];
  while (low < high) {
    while (low < high && arr[high] > pivot) {
      --high;
    }
    arr[low] = arr[high];
    while (low < high && arr[low] <= pivot) {
      ++low;
    }
    arr[high] = arr[low];
  }
  arr[low] = pivot;
  return low;
}

function quickSort2(arr, low, high) {
  if (low < high) {
    let pivot = partition2(arr, low, high);
    quickSort2(arr, low, pivot - 1);
    quickSort2(arr, pivot + 1, high);
  }
  return arr;
  1. 时间复杂度
    (1)最优时间复杂度:O(nlogn)
    (2)最坏时间复杂度:O(n)
    (3)稳定性:不稳定
    五 希尔排序过程
    希尔排序是插入排序的一种,也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
    js 排序方法 - 图9
    1.代码实现:
    function shellSort(arr) {
     var len = arr.length,
         temp,
         gap = 1;
     while(gap < len/3) {          //动态定义间隔序列
         gap =gap*3+1;
     }
     for (gap; gap > 0; gap = Math.floor(gap/3)) {
         for (var i = gap; i < len; i++) {
             temp = arr[i];
             for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) {
                 arr[j+gap] = arr[j];
             }
             arr[j+gap] = temp;
         }
     }
     return arr;
    }
    
  2. 时间复杂度
    (1)最优时间复杂度:根据步长序列的不同而不同
    (2)最坏时间复杂度:O(n)
    (3)稳定性:不稳定
    六. 归并排序
    归并排序是采用分治法(把复杂问题分解为相对简单的子问题,分别求解,最后通过组合起子问题的解的方式得到原问题的解)的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组
    将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,水小九先取谁,取了后相应的指针就往后移一位。然后比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可
    1. 归并排序分析
    js 排序方法 - 图10js 排序方法 - 图11
    代码实现: ``` function mergeSort(arr) { // 采用自上而下的递归方法 var len = arr.length; if(len < 2) {
     return arr;
    
    } var middle = Math.floor(len / 2),
     left = arr.slice(0, middle),
     right = arr.slice(middle);
    
    return merge(mergeSort(left), mergeSort(right)); }

function merge(left, right) { var result = [];

while (left.length && right.length) {
    if (left[0] <= right[0]) {
        result.push(left.shift());
    } else {
        result.push(right.shift());
    }
}

while (left.length)
    result.push(left.shift());

while (right.length)
    result.push(right.shift());

return result;

} ``` 2.时间复杂度
(1)最优时间复杂度:O(nlogn)
(2)最坏时间复杂度:O(nlogn)
(3)稳定性:稳定