常用排序算法思想

常用排序算法思想

我们都知道 java.util 包中 Arrays.sort() 方法,对 int[ ] 数组类型的排序会根据数组的长度及有序度等因素,去选择最优的算法策略对其进行排序。

本篇主要针对下面几种常用的排序算法思路做简单介绍及代码实现 (本文代码均以升序演示)。

  1. 桶排序 :声明一个新数组,每个下标就是一个桶,遍历要排序的数组,依次把元素往对应的桶里扔时,就把那个桶的值加1即可。
  2. 冒泡排序:元素两两比较,小的数往上冒。一直重复这样的操作,直到没有要交换的元素为止。
  3. 快速排序:找一个元素作为基准数,把数组中小于基准数的元素移动到左边,大于的移到右边,遍历一次后就可以找到基准数在数组中该待的位置,然后依次对左右两区进行相同操作即可。
  4. 插入排序:往一个已经排好序的数列中插入一个数,使得插入后数列仍然有序。
  5. 选择排序:在序列中依次选择最小,第二小,第三小…的数放到数列中。
1.桶排序
1.1思想

int[ ] a = new int[5] ;
在这里插入图片描述
桶排序非常有趣,是所有排序算法中最快最简单的排序算法。这些桶桶说白了就是数组的每个下标。遍历要排序的元素时,把元素扔进对应的桶中并将数组指定位置的值加1。最终收集数据时,数组每个位置的值为几就输出几次下标,这样就实现桶排序了。

1.2代码实现
    public static int[] bucketSort(int[] array) {
        //创建10个桶桶
        int[] ints = new int[10];
        //遍历要排序的数组
        for (int i = 0; i < array.length; i++) {
            //给元素对应桶桶的值+1
            ints[array[i]]++;
        }
        return ints;
    }

以上几行代码就已经完成排序了,只是为了便于数据的收集,我们可以对桶桶进行遍历,取那些值不为0的桶桶即可。下标的值是几就说明元素出现了几次。这里给出收集数组排序后的代码。

    public static int[] printt(int[] bucket,int[] array){
        int index = 0;
        //遍历桶桶
        for (int i = 0; i < bucket.length; i++) {
            //不等于0的桶桶原数组才有对应的值
            if(bucket[i] != 0){
                for (int j = 0; j < bucket[i]; j++) {
                    array[index++] = i;
                }
            }
        }
        return array;
    }
1.3总结

桶排序实际上只需遍历一次所有的待排序元素,然后依次放入指定的位置,所以它是最快的排序算法。但是要知道,待排序数组的范围是多大就需要多少个桶桶,当元素跨度范围越大时空间浪费就越大,这是一种典型的空间换时间的做法,虽然这种排序不常用,但多一种思维也是有用武之地的。

2.冒泡排序

在这里插入图片描述

2.1思想

从尾部开始比较相邻的两个元素,如果后元素比前元素小,就交换位置。遍历一次后,就可以把最小的元素冒出来。一直重复这样的操作,冒第二小、第三小…直到冒完所有元素即可。

2.2代码实现
    public static int[] bubbleSort(int[] array) {
        //声明一个变量待会交换的时候用
        int min;
        //遍历要排序的数组
        for (int i = 0; i < array.length - 1; i++) {
            //从最后一个元素开始,向前遍历
            for (int j = array.length - 1; j > i; j--) {
                //两两比较,如果位置不对,就交换位置
                if (array[j] < array[j - 1]) {
                    min = array[j];
                    array[j] = array[j - 1];
                    array[j - 1] = min;
                }
            }
        }
        return array;
    }
2.3总结

冒泡排序用到的额外存储空间只有一个,即用于交换位置的临时变量,就是前面代码的 min 。对于同样大小的元素时并不移动。
其实冒泡排序可以对其进行改,进使它性能更好。比如一次冒两个元素,进行正向和反向两次冒泡比较,即可冒出最大最小两个元素,这里不提供代码实现,感兴趣的动手实现吧。

3.快速排序

在这里插入图片描述

3.1思想

进行一次遍历时,为了方便,我们一般选择区间第一个元素作为基准数。把小于基准数的元素扔到左边,大于基准数的元素扔到右边,这次遍历结束后,基准数应该待的下标位置就找到了。然后分别对左右两边区间进行相同操作,分别重新选择基准数,对区间遍历,找这基准数该待的位置。直到各分区只有一个数为止。

3.2代码实现
    public static int[] quickSort(int[] array, int start, int end) {
        if (start < end) {
            //拿第一个数作为基数,并记录下来防止后面丢了
            int temp = array[start];
            int i = start;
            int j = end;
            while (i < j) {
                //从右边找比基数小的下标位置 j
                while (array[j] >= temp && i < j) {
                    j--;
                }
                if (i < j) {
                    //把小的数丢到 i 位置
                    array[i] = array[j];
                }
                //从左边找比基数大的下标位置 i
                while (array[i] <= temp && i < j) {
                    i++;
                }
                if (i < j) {
                    //把大的数丢到 j 位置
                    array[j] = array[i];
                }
            }
            //当i与j相遇就结束一次遍历,i或j的下标就是基数该待的位置
            array[i] = temp;
            //此时 数组被分为两部分,基数的左边和右边 递归这两部分即可。
            quickSort(array, start, i - 1);
            quickSort(array, i + 1, end);
        }
        return array;
    }
3.3总结

快速排序是跳跃式的交换,交换距离很大。这种算法实际上是一种分治法思想,也就是把问题分为一个个小部分,然后分别解决,最后把结果组合起来。大多数情况下,我们认为快速排序的平均时间复杂度为O(nlogn),空间复杂度为常量。快速排序基本上被认为是所有排序算法中平均性能最好的。

4.插入排序

在这里插入图片描述

4.1思想

插入排序实际上把待排序数组分成了两部分。一部分已排序,另一部分未排序,只要一步步把未排序部分的元素插入到已排序部分,使其仍然有序即可。这里的重点就很明确了,需要找到插入点的下标。

4.2代码实现
    public static int[] insertSort(int[] array) {
        //第一个元素不需要排序,认为在已排列部分中,从第二个元素开始找其插入点
        for (int i = 1; i < array.length; i++) {
            //记录当次要排序的元素
            int temp = array[i];
            int j = i;
            //一旦找到比它小的数,这个小的数的后一位就是要插入的位置
            for (; j > 0 && temp < array[j - 1]; j--) {
                //这段元素都向后挪个位给它
                array[j] = array[j - 1];
            }
            //最后把这个元素放入插入点
            array[j] = temp;
        }
        return array;
    }
4.3总结

很明确,插入排序在数列近似有序时,效率才比较高,因为这样会减少移动次数。总的来说性能不是很好。但它的好处就是占用空间很少,只需要一个额外空间来存储临时变量即可。对于插入算法的优化,只需要围绕如何快速找到插入点即可,比如二分插入,还有一个希尔排序。了解即可,这里不做代码演示。

5.选择排序

在这里插入图片描述

5.1思想

选择排序非常简单,只需要依次选择最小,放到第一位;选择第二小,放到第二位…直到选完所有元素即可。

5.2代码实现
    public static int[] selectSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            //记录最小数的值
            int temp = array[i];
            //记录最小数的位置
            int minIndex = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            //找到当次最小值的位置后,交换位置
            array[i] = array[minIndex];
            array[minIndex] = temp;
        }
        return array;
    }
5.3总结

选择排序的重要步骤就是在待排序的数列中寻找最小(或最大)的元素,可以从这个出发点去找优化方案。另外,选择排序不需要一个个挪动元素,而是直接交换。相比于冒泡排序来说,它性能会好一些。

各排序算法性能对比

最后附上各排序算法的平均时间复杂度、空间复杂度及稳定性的对比表。

排序算法平均时间复杂度空间复杂度稳定性
桶排序O(n+m)O(m)取决于桶内排序的算法
冒泡排序O(n2)O(1)稳定
快速排序O(nlogn)O(logn)不稳定
插入排序O(n2)O(1)稳定
选择排序O(n2)O(1)不稳定

动态图来源于网络

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值