排序算法学习总结

## 排序算法

### 首先介绍一下内排序和外排序的区别

 外排序:指排序过程中排序对象不能同时放在内存当中,排序过程不断在内外存之间移动的排序 内外存结合

 内排序:排序过程中所有元素都放到内存中进行排序

 外排序用读写外存的次数衡量其效率,内排序根据比较次数来衡量效率

排序算法的稳定性

 假定待排序的记录序列中,存在多个具有相同关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,若r[i] == r[j],且r[i]在r[j]之前,在排序之后 r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则是不稳定的。

 

1. 快速排序

 快速排序是对冒泡排序的一种改进,通过在数组中取出一个数据第一趟排序时将数据分为俩个部分,大于该数据的放一侧,小于该数据的放另一侧,该部分需要O(n)次数的操作,然后递归调用,在俩侧都实现快速排序。该过程

 该排序算法属于内排序,不稳定

平均时间复杂度为 O(n log n)

最好情况为: O(n log n)

最坏情况: O(n²)

空间复杂度 O (log2 n)

简析:因为排序的次数不大于比较的次数,因此快速排序的最坏时间复杂度为O(n^2)

因为时间复杂度的分析只需关注循环执行次数最多的一段代码,这段核心代码的执行次数的n的量级就是整段要分析代码的时间复杂度。

for(var i = 0; i < arr.length; i++){
      if(arr[i] < midNum){
        left.push(arr[i])
      } else {
        right.push(arr[i])
      }
    }

以上是快速排序的核心代码,也是执行次数最多的一段代码,其中外层for循环的次数为所要排序数组的长度n,则T1(N) = O(n)

内层当arr[i]的值的大小满足一定条件时,才继续执行对应的代码这是一个对数阶的时间复杂度(所有对数阶的时间复杂度都可以记做O(logn)),记为T2(N) = O(logn)

所以总的时间复杂度为T(n) = T1 * T2 = O(nlogn)
 

//快速排序
function quickSort(arr){
    //边界值判断
    if(arr.length <= 1){
        return arr;
    }
    var left = [],
        right = [],
        len = arr.length,
        midIndex = Math.floor(arr.length / 2),
        midNum = arr.splice(midIndex, 1)[0];
    for(var i = 0; i < len; i++){
        if(arr[i] < midNum){
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
return quickSort(left).concat([midNum], quickSort(right));
}

2. 冒泡排序

 冒泡排序是通过比较俩个相邻的元素,如果第一个元素大于第二个元素,那么进行交换(升序),降序相反

 冒泡排序算法属于内排序,稳定

 平均时间复杂度为 O( n²)

 最坏情况为 O( n²)

 最好情况为 O(n)

 空间复杂度为 O(1) 是一个就地排序

 关于冒泡排序算法的优化

只要内循环某一趟没有进入 arr[j] > arr[j + 1]的话,说明此时该数组已经排序完成,没有必要再消耗性能继续遍历已排序好了的数组了

//已优化好了的排序算法
//* 
* arr: 待排序数组
* type: 定义排序类型,默认为true(升序), false(降序)
*/
function bubble(arr,type){
    var i,
        j,
        temp,
        len = arr.length,
        flag = true; // 判断是否已排序好
    flag = flag || true;
    for(; i < len - 1; i++){
        flag = true;
        for(; j < len - i; j++){
            if(type){
                if(arr[j + 1] < arr[j]){
                    temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                    flag = false;
                } 
            }
            if(flag){
                flag = false;
            }
        }
    }

3. 选择排序

 首先在未排序序列中找到最大(小)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最大(小)元素,然后放到已排序序列的末尾。以此类推,直到所有元素都排序完。

 在i之前的元素是已经排序好了的,而在i之后是尚未排序好的。

 选择排序属于内排序 不稳定

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

最好情况:O(n²)

最坏情况:O(n²)

可见选择排序在时间复杂度上无论什么数据进去都是O(n^2),数据规模越小越好,好处是不占用额外的内存。

 

3. js中自带的sort方法

JavaScript中的排序方法是基于原数组进行排序,不生成副本,也就是说这个方法是会改变原数组的,该方法有一个可选的参数 sortby ,用来规定排序顺序。而且该参数必须是函数,如果没有使用参数,那么sort方法将会根据字符串编码的方式进行排序。

当有使用该参数时,向该比较函数中传入俩个参数,a、b,用来定义排序规则,比如

`function ascent (a, b) {

return a - b;//升序

}

function descent (a, b) {

return b - a;//降序

}`

 

4.插入排序

算法思路:

类似于整理扑克的过程,将当前刚拿到的牌插入到已排序好的牌组中,插入排序有N-1趟排序组成,将该元素与之前排序好的数组的每一项比较,并将其放入合适的位置,最终得出有序数组。

基于该算法的思路来看,插入排序存在最好情况和最坏情况,以升序排序举例

最好情况 升序排列的数组 比较n-1次即可

最坏情况 降序排列的数组 比较n(n-1)次

时间复杂度:O(n^2) 这是一种稳定的排序方法

 

5.归并排序

算分思路:

将数组中n个记录看成n个长度为1的有序字表

进行俩俩归并使记录关键字有序,得到 n/2个长度为2的有序字表

重复第二步操作,直到所有记录都归并成一个长度为n的有序表为止。

function mergeSort(array){
    var len = array.length;
    if(len < 2){
        return array;
    }
    var middle = Math.floor(len / 2),
    left = array.slice(0, middle),
    right = array.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;
    }
}

6.堆排序

堆排序(英语:Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

复杂度:O (nlgn)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值