常见的八大排序算法

概念

我们平常说的排序,通常都是按照从小到大的顺序,而且指的是原地排序(in place sort)。
排序又分内部排序和外部排序:
内部排序——将数据拉到内存中进行排序
外部排序——将数据放到磁盘排序,数据量太大,导致在内存当中不可存放
以下介绍的排序都是基于比较的内部排序:

标准

判断排序的标准:时间复杂度、空间复杂度、稳定性

这里重点说稳定性:如果一个序列中有若干相等的数据,排序之后其相对位置没有发生改变排序就是稳定的。

比较快速判断是否稳定的方法:如果在排序过程中没有发生跳跃式交换(隔几个数地去交换),就是稳定的。

1. 直接插入排序:


思想:

  1. 假定第一个元素是有序的,从第二个元素开始排序
  2. i 遍历无序序列,将 i 的值赋给tmp
  3. 从tmp前一个元素开始从后往前遍历有序序列,寻找合适位置插入
  4. 若tmp比当前元素大或相等:说明前面的序列已经有序,tmp之间插到当前位置即可; 若tmp比当前元素小:当前元素向后移,前面元素重复比较,直到找到对应位置

代码实现:

//直接插入排序:每次和前面的数据从后往前比较,找合适位置插入
//时间复杂度:最坏情况下为O(n^2),最好情况下为O(n)(有序)
//空间复杂度:O(1)
//稳定性:稳定的
//特点:数据越有序,用直接插入排序越快
public static void insertSort(int[] arr) {
   
    for(int i = 1; i < arr.length; i++) {
   
        int tmp = arr[i];
        int j;
        for(j = i-1; j >= 0; j--) {
   
            //if里的判断条件有等号的不稳定,去掉等号是稳定的-》看作是稳定的
            if(arr[j] > tmp) {
   
                arr[j+1] = arr[j];
            }else {
   
                //前面已经有序
                break;
            }
        }
        arr[j+1] = tmp; 
    }
}

优化:折半插入排序——前面的区间已经有序,可以使用二分查找来找。当然了,这里所说的二分查找要稍作改变。

//未排序元素在排序序列中寻找合适位置时,若在此过程中能应用二分查找,速度会大大提高
public static void binarySearchInsertSort(int[] arr) {
   
    for(int i = 1; i < arr.length; i++) {
   
        int tmp = arr[i];
        //[left,right)
        int left = 0;
        int right = i;

        while(left < right) {
   
            int mid = (left+right)/2;
            if(arr[mid] > tmp) {
   
                right = mid;
            }else {
   
                left = mid+1;
            }
        }
        for(int j = i; j > left; j--) {
   
            arr[j] = arr[j-1];
        }
        arr[left] = tmp;
    }
}

2.希尔排序


思想:

将序列分为很多组,各自组内排序之后,接着分组,分很多次组,但是不管分几次,最后一次那个组一定是整体看成一组 (此时序列已接近有序)。

分组原则:

  1. 分组的增量序列都是质数,且最后一个分组应是1。
  2. 不是等分的组,因为直接插入排序是越有序越快
  3. 分多少次组,每组怎么分才能最快这些都没有明确规定,因为数据大小不确定,无法得出结论。

在这里插入图片描述
在这里插入图片描述

代码实现:

//希尔排序(分组思想)
//时间复杂度:最坏情况下为O(n^2),最好情况下为O(n)
//空间复杂度:O(1)
//稳定性:不稳定的(有跳跃式交换)
public void shell(int[] arr,int gap) {
   

    for(int i = gap; i<arr.length; i++) {
   
        int tmp = arr[i]; 
        int j;
        for( j = i-gap; j >= 0; j -= gap) {
   
            if(arr[j] > tmp) {
   
                arr[j+gap] = arr[j];
            }else {
   
                break;
            }
        }
        arr[j+gap] = tmp;
    }
}
public void shellSort(int[] arr) {
   
    int[] drr = {
   5,3,1};
    for(int i = 0; i < drr.length;i++) {
   
        shell(arr,drr[i]);
    }
}

3. 选择排序


思想:

每次从待排序数字的后面选取一个比当前数字小的数据进行交换,直到当前序列遍历完成。
用 i 来遍历序列,每一趟排序用 i 位置的元素和后面的无序序列元素依此比较大小,将较小的换到 i 位置,一趟排序过程后,无序序列中最小的元素换到 i 位置处。
相较于其他排序算法,选择排序思想比较简单,实现容易。

//选择排序
//时间复杂度:O(n^2) 不分好坏
//空间复杂度:O(1)
//稳定性:不稳定的(有跳跃式交换)
public void selectSort(int[] arr) {
   
    for(int i = 0; i < arr.length; i++) {
   
        for(int j = i+1; j < arr.length; i++) {
   
            if(arr[j] < arr[i]) {
   
                int tmp =arr[i];
                arr[i] =arr[j];
                arr[j] = tmp;
            }
        }
    }
}

4. 堆排序


思想:

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大/小的数。
实质是利用了堆底层是完全二叉树,大根堆堆顶元素是二叉树的最大值,小根堆堆顶元素是二叉树的最小值的性质。

注意: 排升序要建大堆;排降序要建小堆

下面以排升序为例:

  1. 先将堆排序序列,建立为大根堆
  2. 每次拿堆顶元素和最后一个待排序序列的数据进行交换,然后调整0号下标这棵树
    
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值