常用排序算法 Java 实现

本文是对《算法 第四版》中排序章节的总结,包括 选择排序,插入排序,希尔排序,归并排序,快速排序,堆排序和冒泡排序

各种排序算法的性能特点

有多种排序算法存在,就是因为各种算法拥有不同的性能特点,各有所长,适用于不同场合,下面是书中对各种排序算法的性能特点的总结:

算法 时间复杂度 空间复杂度 是否稳定
选择排序 最差:N^2,平均:N^2,最优:N^2 1 不稳定
插入排序 最差:N^2,平均:N^2,最优:N 1 稳定
希尔排序 最差:N*logN,平均:N*logN,最优:与递增序列有关 1 不稳定
快速排序 最差:N^2,平均:N*logN,最优:N*logN lgN 不稳定
归并排序 最差:N*logN,平均:N*logN,最优:N*logN N 稳定
堆排序 最差:N*logN,平均:N*logN,最优:N*logN 1 不稳定
冒泡排序 最差:N^2,平均:N^2,最优:N 1 稳定

本文使用 Java 实现以上几种排序算法,并对《算法 第四版》书中的代码稍有修改,作为演示,只针对 int[] 类型进行排序,因此文中排序算法的输入源都是 int[] 类型,并且将一些公共方法抽离出来,比如比较两个数大小的 less() 和交换两个数的 exchange() 方法,公共方法放在抽象类 SortModel.java 中,其他具体的排序方法只需要继承它,并实现自己特有的 sort() 方法即可

模版

将排序算法的公共方法放在一个抽象类中,具体的排序算法类只需要继承自这个抽象类,并实现自己的 sort() 方法即可,具体代码如下:

public abstract class SortModel {
   

    //记录排序消耗的时间
    protected long usedTime = 0;

    //具体的排序方法,由子类实现
    protected abstract void sort(int[] a);

    //比较 a 和 b 的大小,如果 a 小于 b,则返回 true
    protected boolean less(int a, int b) {
        return a < b;
    }

    //交换数组中的两个数
    protected void exchange(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    //打印数组
    protected void show(int[] arr, int count) {
        System.out.println("\n\n使用 " + getSortMethod() + " 对 " + count + " 个数排序用时: " + usedTime + "ms");
        for (int i : arr) {
            System.out.print(" " + i);
        }
    }

    //获取当前使用的排序方法名称
    protected abstract String getSortMethod();
}

接下来开始总结这些排序算法的具体实现

选择排序

选择排序是比较基础的排序算法,也是一种很容易想到的排序算法,具体描述是这样的:首先找到数组中最小的元素(这是一个遍历比较的过程),然后将它和数组中的第一个元素交换位置,接着在剩下的元素中找到最小的元素,将它与第二个元素交换位置,如此循环,直到将整个数组排序完成

在选择排序中一个主要的操作就是在数组中找到最小的元素,如何在一个给定的数组中找到值最小的那个元素呢?这个过程分为两步:

  1. 遍历:
    遍历最简单的形式就是使用一个 for 循环,从开始索引,到结束索引,依次访问数组中的元素

  2. 比较:
    比较至少需要两个元素,在遍历的时候,每次只访问数组中的一个元素,因此为了能够比较,需要有一个临时索引指向的元素来和当前访问的元素进行比较,如果当前元素小于临时索引指向的元素,就把当前元素的索引赋值给临时索引

通过以上这两步,在遍历中比较,在满足当前元素小于临时索引指向的元素的条件时,就将当前元素的索引赋值给临时索引,如此循环,遍历结束后,临时索引指向的元素值便是最小元素值

假设有一组数:16,13,18,11,14,12

找最小元素的过程是:

默认临时索引为数组第一个元素,即索引为 0,开始遍历数组

  • 当前元素是索引为 0 的元素 16,和临时索引为 0 的元素比较,相等,不赋值

  • 当前元素是索引为 1 的元素 13,和临时索引为 0 的元素比较,小于,当前索引赋值给临时索引

  • 当前元素是索引为 2 的元素 18,和临时索引为 1 的元素比较,大于,不赋值

  • 当前元素是索引为 3 的元素 11,和临时索引为 1 的元素比较,小于,当前索引赋值给临时索引

  • 当前元素是索引为 4 的元素 14,和临时索引为 3 的元素比较,大于,不赋值

  • 当前元素是索引为 5 的元素 12,和临时索引为 3 的元素比较,大于,不赋值

遍历结束,临时索引为 3,因此索引为 3 的元素就是这个数组中的最小元素

以上是一次寻找最小元素的过程,需要执行的比较次数与遍历的数组长度成正比,如果遍历的数组长为 N,则查找最小元素需要 N 次比较

选择排序的排序过程是:

  • 遍历索引[0-5],找到最小元素 11,对应的索引为 3,将它与索引为 0 的元素交换,交换后如下

  • 遍历索引[1-5],找到最小元素 12,对应的索引为 5,将它与索引为 1 的元素交换,交换后如下

  • 遍历索引[2-5],找到最小元素 13,对应的索引为 5,将它与索引为 2 的元素交换,交换后如下

  • 遍历索引[3-5],找到最小元素 14,对应的索引为 4,将它与索引为 3 的元素交换,交换后如下

  • 遍历索引[4-5],找到最小元素 16,对应的索引为 4,将它与索引为 4 的元素交换,交换后如下

  • 遍历索引[5-5],找到最小元素 18,对应的索引为 5,将它与索引为 5 的元素交换,交换后如下

以上是选择排序的过程,从这个过程中可以分析到,在一次选择排序过程中需要* N+(N-1)+(N-2)+…+3+2+1 = N(N-1)/2 ~ N^2/2* 次比较和 N 次交换,需要的比较次数属于 N^2 级别。并且,在选择排序中不存在最优与最坏情况,无论输入的数据情况怎样,选择排序都需要固定次数的比较和交换,

选择排序有两个特点:

  • 运行时间和输入无关
    从上面的分析可以知道,选择排序中不存在最优情况和最坏情况,即使输入数据已经整体有序,选择排序所需要的比较和交换次数依然是固定的,只和输入数组的大小有关

  • 数据移动次数最少
    选择排序所需的交换次数和输入数组的大小有关,如果输入数组大小为 N,则选择排序所需交换次数为 N

代码实现


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值