基础排序算法

种类:插入排序、选择排序、归并排序、堆排序、快速排序、计数排序
    、桶排序、基数排序、冒泡排序、希尔排序、梳排序 

一、插入排序

对数进行从小到大排序时,需要将一个数放到前面,而将那些比他大的数挤到后面,来实现排序。

过程分析(6,5,3,1,8,7,2,4)

  • 将数列划分为两个部分,有序和无序,即将数列中的第一个元素(6)看作有序的部分,后面的其他数(5,3,1,8,7,2,4)看做无序的部分.

  • 从前到后扫描有序部分,将无序部分中的第一个元素插入到有序的部分中合适的位置

  • 然后将有序序列该数后面的数依次后移一位,形成新的有序部分

  • 重复以上步骤,直到遍历完整个数列

js代码

<script>
var arr = [3,2,5,8,4,7,6,9];
function sort(arr) {
    //从小到大排列:假设第0个元素是有序数列,第一个以后的元素是无序数列,
    // 从第一个元素起将无序数列的元素插入到有序数列中
    for (var i = 1; i < arr.length; i++) {
        var key = arr[i];
        var j = i - 1;//j
        //若后一个元素小于前一个元素;
        while (j >= 0 && key < arr[j]){
          //把大的值赋给后面的元素
            arr[j + 1] = arr[j];
          //j从后向前循环
            j--;
        }
        //将小的值赋给跳出上个循环的后一个值
        arr[j + 1] = key;
    }
    return arr;
}
sort(arr);
console.log(arr);
</script>

空间复杂度

O(n)用于存储整个数列,O(1)辅助,暂存操作数,用于插入。

时间复杂度

  • 最好:已经有序的情况下,只需要遍历数列一次,为O(n)。

  • 最坏:反序情况下比较次数依次是1 + 2 + 3 + … + (N - 1),即(1/2)n(n-1)次。O(n^2)。

  • 平均:O(n^2)

二、选择排序

在未排序序列每次选择出最小的数,和排在第一个的数进行交换。从而达到将较小的数排在前面的目的。

过程分析(从小到大)

  • 在未排序的序列中找到最小的元素,存放到序列起始的位置

  • 再从未排序的序列中寻找最小的元素,然后放到序列末尾

  • 重复第二步,直到所有的元素都排列完毕

这里写图片描述

js代码

<script>
    var arr=[3,2,5,8,4,7,6,9];
    function selectionSort(arr){
        //定义未排序数组的最小值
        var minIndex ,temp;
        for(var i = 0;i < arr.length;i++){
            minIndex = i;
            //寻找未排序序列的最小值
            for(var j = i+1;j < arr.length;j++){
                if(arr[j] < arr[minIndex]){
                    minIndex = j;
                }
            }
            //将找到的最小值插入到已排序序列的后面
            temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
        return arr;
    }
    selectionSort(arr);
    console.log(arr);
</script>

空间复杂度

O(n)用于存储整个数列,O(1)辅助,暂存操作数,用于交换。

时间复杂度

由于一共有n个位置要进行选择,每次选择时又要对剩下为放入合适位置的数进行便利,所以时间复杂度不分最好和最坏,依次比较(N-1)+(N-2)+ … +1次,即(N-1+1)*N/2 = (N^2)/2, 为O(n^2)。

三、归并排序

归并算法主要通过拆分和合并两个步骤来完成对数列的排序,将需要排序的数列拆分成尽量小的序列,然后重新合并,合并时按顺序排列,最终形成有序序列。

过程分析

①迭代法

  • 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  • 设定两个指针,最初位置分别为两个已经排序序列的起始位置

  • 比较两个指针所指向的元素,选择相对小的元素放入到合并的空间,并移动指针到下一位置

  • 重复步骤3直到某一指针到达序列尾

  • 将另一序列剩下的所有元素直接复制到合并列尾

②递归法

  • 假设序列共有n个元素

  • 将序列每相邻两个数字进行归并操作,形成n/2个序列,排序后每个序列包含四个元素

  • 将上述序列再次归并形成n/4个序列,每个序列包含四个元素

  • 重复步骤2,直到所有元素排序完毕

这里写图片描述

js代码

四、冒泡排序

冒泡排序中较大的数与较小数交换了位置。冒泡排序是一种用时间换空间的算法。

过程分析

  • 比较相邻的元素,如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻的元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的元素。
  • 针对所有的元素重复以上步骤,除了最后一个
  • 持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。

这里写图片描述

js代码

<body>
<script>
   var arr = [3,2,5,8,4,7,6,9];
    function bubbleSort(arr){
    //控制交换的趟数
        for(var i = 0;i<arr.length;i++)
          //targ是做为一个开关来使用;如果这一趟没有做交换,则targ=0;跳出循环
            var targ = 0;
            for(var j = 0;j<arr.length;j++){
            //将相邻的两个数进行比较,若满足则进行交换
                if(arr[j]>arr[j+1]){
                    var temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                    targ = 1;
                }
            }
            if(!targ){
                break;
            }
        }
        return arr;
    }
    bubbleSort(arr);
    console.log(arr);
</script>
</body>

时间复杂度

  • 外层循环n-1次,内层循环n-i次。内层循环总的次数用等差数列求和公式是(1+(n-1))(n-1)/2=n(n-1)/2≈n^2/2,外层循环赋值次数为常数设为a,内层循环赋值次数也是常数设为b,所以f(n)≈a * n + b * n^2/2,时间复杂度是O(n^2).

  • 最好:O(n)

  • 最坏:O(n^2)

  • 平均:O(n^2)。

稳定性:稳定

五、快速排序

在快速排序中也有一个基准–枢轴(pivot),其他的数根据大小排在它的两边。快速排序在最好情况和一般情况下的时间复杂度都是最低的。

过程分析

  • 从数列中挑出一个元素,称为“基准”(pivot)

  • 重新排序数列,所有的元素比基准值小的放在基准值的前面,比基准值大的放在基准的后面,这个过程结束后,基准就处于数列的中间位置

  • 用递归把小于基准值元素的子数列和大于基准值元素的子序列排序

    复杂度低的方式
    
//交换两个数,原地交换,而不是用两个空数组
function swap(array, a, b) {
     [array[a], array[b]] = [array[b], array[a]];
 }

 function quick(array, left, right) {
     let index;
     if (array.length > 1) {
         index = partition(array, left, right);
         if (left < index - 1) {
             quick(array, left, index - 1);
         }
         if(index < right) {
             quick(array, index, right);
         }
     }
     return array;
 }
 function quickSort(array) {
     return quick(array, 0, array.length - 1);
 };

 function partition(array, left, right) {
     const pivot = array[Math.floor((right + left) / 2)];
     let i = left;
     let j = right;

     while (i <= j) {
         while (compare(array[i], pivot) === -1) {
             i++;
         }
         while (compare(array[j], pivot) === 1) {
             j--;
         }
         if (i <= j) {
             swap(array, i, j);
             i++;
             j--;
         }
     }
     return i;
 }

 function compare(a, b) {
     if (a === b) {
         return 0;
     }
     return a < b ? -1 : 1;
 }

阮一峰版本:因为有了splice和concat,每次递归都会多触发两个数组,导致空间复杂度变大

var quickSort = function(arr) {
  if (arr.length <= 1) {
    return arr;
  }

  var pivotIndex = Math.floor(arr.length / 2);

  var pivot = arr.splice(pivotIndex, 1)[0];

  var left = [];

  var right = [];

  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  sum = right.length + left.length + sum;
  return quickSort(left).concat([pivot], quickSort(right));
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值