通用方法
为了使排序算法更有适用性,所以不是简单的使用Int数组,而是使用Comparable。Java中的String,Integer,Byte类都实现了Comparable接口,所有这些类都可以使用这些排序算法。
交换数组的i,j索引位置的元素
1 | private static void exch(Comparable[] a,int i,int j){ |
判断一个元素是否小于另一个元素
1 | private static boolean less(Comparable v,Comparable w){ |
展示排序的结果
1 | private static void show(Comparable[] a){ |
选择排序
选择排序的算法就是每次选择数组中的最小(最大)的元素放在数组的最开始(最末端)。
1 | public static void selection(Comparable[] a){ |
插入排序
插入排序算法是从索引0开始,将每个索引的元素放在左侧合适的位置,使得当前索引的左侧永远是有序的。
1 | public static void insertion(Comparable[] a){ |
希尔排序
希尔排序是按照一定的步长,将数组的元素分成若干组,将这若干组中的元素排序,再取比上一个步长小的步长再一次排序,直到步长为1。在对分组元素排序的时候相当于是一个简化的插入排序。
步长的选择对希尔排序的性能影响很大。
时间复杂度小,没有使用额外的空间,排序算法的优先考虑
1 | public static void shell(Comparable[] a){ |
和普通的插入排序相比,希尔排序在步长为1的时候整个数组已经趋近于有序的了,在移动交换元素的时候使用的时间会比插入排序更少。但是希尔排序也是不稳定的。
归并排序
归并排序的思想就是把一个数组分为左右两个部分,分别对这两个部分进行排序,再对这两个有序的左右部分合并。而利用递归的思想,可以把数组一直划分为左右两个部分,直到左右两个部分都只包含了一个元素(这个时候,左右两个部分都是有序的)再依次合并左右两个部分的数组。
利用递归思想划分数组
1 | //定义一个额外的数组,在合并左右两个数组的辅助 |
合并两个有序的数组(合并a[lo..mid]和a[mid+1,hi])
1 | public static void merge(Comparable[] a,int lo,int mid,int hi){ |
归并排序就是通过递归不断把数组分为左右两个部分,直到左右两个部分都只包含一个元素的时候,合并两个数组。算法的关键部分就是掌握合并数组是可能出现的4种情况。
快速排序
快速排序是一种对随机数组排序较优的算法。
主要思路:先随机选取一个元素j,在数组的开始和结尾都各有一个指针(双指针),在遍历到左边有一个元素lo大于j,右边有一个元素hi小于j,将这两个lo和hi交换位置。直到左右两个指针相遇。这个时候数组有一下的性质:
1.元素j已经处于排序以后的位置
2.元素j左边的元素都小于j
3.元素j右边的元素都大于j
采用递归的思想,对左右两边的元素进行上述操作。
递归操作
1 | public static void sort(Comparable[] a){ |
选取元素,划分数组
1 | public static int partition(Comparable[] a,int lo,int hi){ |
快速排序算法的优化:
1、在切分到比较小的数组的时候,切换为插入排序,节省时间。
2、在一开始选取元素的时候,通过计算中位数,来确保划分数组时左右两边数组大小是接近的。
3、在对于有很多重复元素的数组的时候,划分数组分为大于、小于、等于这三个部分,而不只是换分为大于、小于两个部分,就可以减少对重复数据递排序的时间。