普通排序

对常见的排序记录一下,后面忘记了再回头看,代码是用Java写的 项目地址

高级排序
线性排序

最好时间复杂度最坏时间复杂度平均时间复杂度空间复杂度稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
插入排序O(n)O(n^2)O(n^2)O(1)稳定
希尔排序O(n)O(n^2)O(n^ 3/2)O(1)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
快速排序O(nlogn)O(n^2)O(nlogn)O(logn)不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定

注:希尔排序的时间复杂度没有定论,上面的写的时间复杂度具有争议。

冒泡排序和快速排序属于交换排序,选择排序和堆排序属于选择排序,插入排序和希尔排序属于插入排序。三个O(n^2) 级别的排序中插入排序是最优的,插入排序是稳定的排序,而且数据有序度较高时用时会比较短。三个O(nlogn)级别的排序中快速排序是最快的,用的比较多,O(logn)的空间复杂度主要是递归创建变量的开销基本可以忽略,可以看作是原地排序,快排通过选合适的基准值几乎可以避免时间复杂度到最坏的情况。希尔排序作为普通排序打破了O(n^2) 的时间复杂度,会比O(n^2) 好很多,但是数据量很大的情况还是使用高级排序。

下面排序默认按照从小到大排序,data为int数组

// 交换数组中两个数 
swap (int[] data, int i, int j) {
	int temp = data[i];
  data[i] = data[j];
  data[j] = temp;
}
冒泡排序
    public void sort(int[] data) {
        sort(data, data.length);
    }

    private void sort(int[] data, int n) {
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < n - i; j++) {
                if (data[j] > data[j + 1]) {
                    swap(data, j, j + 1);
                }
            }
        }
    }

原理:内层循环每次比较相邻的两个数大小,大的向后移动,内层循环完后最后一个数便是最大的数了。外层循环控制次数,总共进行n-1趟,则全部有序了。内层循环每次j<n-i,因为每次比较完后数组中最后一个已经是最大的了,不需要再比较了。

选择排序
    public void sort(int[] data) {
        sort(data, data.length);
    }

    private void sort(int[] data, int n) {
        for (int i = 0; i < n - 1; i++) {
            int k = i;
            for (int j = k + 1; j < n; j++) {
                if (data[j] < data[k]) {
                    k = j;
                }
            }
            if (i != k) {
                swap(data, i, k);
            }
        }
    }

原理:内层循环寻找最小值然后记录找到最小值的下标,在内层循环结束后交换使得最小值放到正确的位置。外层循环控制次数,总共进行n-1趟,则全部有序了。内存循环每次j=i+1,因为每次比较完后数组中第一个数已经是最小的了,不需要再比较了。

插入排序
    public void sort(int[] data) {
        sort(data, data.length);
    }

    private void sort(int[] data, int n) {
        for (int rightShift = 1; rightShift < n; rightShift++) {
            int compareData = data[rightShift];
            int leftShift = rightShift - 1;
            while (leftShift >= 0 && data[leftShift] > compareData) {
                data[leftShift + 1] = data[leftShift];
                leftShift--;
            }
            data[leftShift + 1] = compareData;
        }
    }

原理:插入排序看成是将原来数组分成两个数组,前面一个是一个有序的数组,后面是无序的,然后将后面无序中每次取出一个插入到前面有序数组中合适的位置,循环n-1次后面数组中数据都被取出插入到前面有序数组中了,则整个数组有序了。

过程:上面代码默认数组中第一个数为有序的,然后取数组中第2个数(compareData)向前和第1个数(data[leftShift])比较,如果第2个数比第1个数小就将第1个数移到第2个数所在的位置,此时数组中第1个数和第2个数则都是第1个数的值,leftShift减1后为-1小于0内层循环结束,然后执行data[leftShift + 1] = compareData将数组第2个数赋值给第1个数,此时数组中前两个数有序了。rightShift加1,compareData取第3个数向前和前面已经有序的数组比较,如果leftShift没到最左边且compareData比前一个小则将前一个数向后一位赋值(最开始比较的则赋值到compareData所在位置),然后继续向前比较,符合上面条件则再向后赋值,只要有一个数碰到一个数比compareData小则内层循环终止(因为前面都是有序的,只要最后一个数比compareData小则前面的数比compareData都要小),然后执行data[leftShift + 1] = compareData,将compareData赋值到最后比较数的那个位置,此时前面有序数组中又插入一个数,一直循环结束则有序了。

特点:1、插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。因为有序时内层循环data[leftShift] 一直小于 compareData,所以只有外层循环的遍历时间,时间复杂度为O(n)。

2、当数据较少时,插入排序速度比较快

3、插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

希尔排序
    public void sort(int[] data) {
        sort(data, data.length);
    }

    private void sort(int[] data, int n) {
        for (int step = n / 2; step > 0; step /= 2) {
            for (int rightShift = step; rightShift < n; rightShift++) {
                int compareData = data[rightShift];
                int leftShift = rightShift;
                while (leftShift - step >= 0 && data[leftShift - step] > compareData) {
                    data[leftShift] = data[leftShift - step];
                    leftShift = leftShift - step;
                }
                data[leftShift] = compareData;
            }
        }
    }

原理:通过将原数组分解为多个较小的子数组,每个子数组使用插入排序进行排序。接着依次缩小增量继续进行排序,待整个序列基本有序时,再对全体元素进行插入排序。

希尔排序又称缩小增量排序,主要是针对插入排序上面三点特点对插入排序的优化。主要是将较大的数据分为多个小数组进行插入排序,而且几次下来数据也基本有序,最后一次进行全量插入排序就非常快了。

过程:以一组数据大体看上面代码步骤,数组[5, 3, 8, 4, 2, 6, 3, 1],8个数据,步长step=n/2=4,外层循环第一遍过程可以看作通过步长4将原数据分为4个小数组[5,2],[3,6],[8,3],[4,1],每个数组进行插入排序后为[2,5],[3,6],[3,8],[1,4],即整个数组变为[2, 3, 3, 1, 5, 6, 8, 4]。然后步长除以2,外层循环执行第二遍,将数据分为2个小数组[2,3,5,8],[3,1,6,4],这两个数组再进行插入排序后为[2,3,5,8],[1,3,4,6],即整个数组变为[2, 1, 3, 3, 5, 4, 8, 6]。然后步长除以2,此时步长为1了,外层循环执行最后一遍,此时数据不用分了也可以说分为1个数组了,此时就是上面的插入排序了,执行完后数据有序[1, 2, 3, 3, 4, 5, 6, 8]。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值