下面是一些基本排序算法的比较:
几种常用的排序算法的比较
快速排序算法是基于“分治算法”的排序算法,其基本思路是:将待排序的集合数组取一个元素叫基数,将其分成2个子数组,是基数左边的数都不大于该基数,而右边的数都不小于该基数;然后递归这一分解过程,并对子数组排序,而当所有的子数组都有序时,整个数组也就自然有序了。
快速排序算法也是一个脆弱的排序算法,在对一个基本有序或已排序的数组做反向排序时,该算法会得到最坏时间复杂度——O(N*N),最好的时间复杂度为NlogN,平均时间复杂度接近于最好的时间复杂度NlogN,其空间复杂度为lgN。
为了使快速排序获得最好的排序性能,最好在排序之前将之做一次“shuffle(洗牌)”操作,将原有顺序打乱之后再排序。
快速排序的整个代码如下:
/** * 排序的基类 * @author jacky * */ public class SortBase{ /** * 将数组a中的数据顺序打乱 * @param a */ public static void shuffle(Comparable[] a) { int N = a.length; int r = 0; for (int i = 0; i < N; i++) { r = i + (int) (Math.random() * (N-i)); exch(a, i, r); } } /* * 将a[i]和a[j]交换顺序 */ public static void exch(Comparable[] a, int i, int j) { Comparable swap = a[i]; a[i] = a[j]; a[j] = swap; } /** * 判断v1是否小于v2 * @param v1 * @param v2 * @return true:小于;false:不小于 */ public static boolean less(Comparable v1,Comparable v2){ return v1.compareTo(v1) < 0; } }
public class QuickSort extends SortBase{ public static void sort(Comparable[] a){ //为获得接近于最好的排序性能(最好的时间复杂度),首先将原数组“洗牌" shuffle(a); //开始排序 sort(a,0,a.length - 1); } /** * 递归调用排序子数组 * @param a * @param lo * @param hi */ private static void sort(Comparable[] a,int lo,int hi){ if(hi <= lo) return; //获取一个数组中的取快速切分的基数的下标,使其左边的元素不大于该数, //其右边的元素不小于该数 int j = partition(a,lo,hi); //将左半部分a[lo,j - 1]排序 sort(a,lo,j - 1); //将右半部分a[就+ 1,hi]排序 sort(a,j + 1,hi); } /** * 在数组a的小下标lo和hi范围内获取快速切分的基数的小标,使该基数的左边的 * 元素都不大于该数,而右边的元素都不小于该数 * @param a * @param lo * @param hi * @return 快速切分基数的下标 */ public static int partition(Comparable[] a,int lo,int hi){ //将数组切分为a[lo...i - 1],a[i],a[j + 1,hi] int i = lo,j = hi + 1; //设置左右扫描指针 Comparable v = a[lo]; //切分元素 while(true){ while(less(a[++i],v)){ if(i == hi) break; } while(less(v,a[--j])){ if(j == lo) break; } if(i >= j){ break; } exch(a,i,j); } exch(a,lo,j); //将v = a[i]放入正确的位置 //a[lo...j - 1] <= a[j] <= a[j + 1 ... hi] return j; } }
快速排序与归并排序相比,一般情况下速度要快于归并排序,但是对于小数组,其排序速度要较插入排序慢一些。对于在给小数组排序时,快速排序的排序速度要较插入排序慢这一特点,我们可以对快速排序算法做如下改进:
public class QuickSort extends SortBase{ public static void sort(Comparable[] a){ //为获得接近于最好的排序性能(最好的时间复杂度),首先将原数组“洗牌" shuffle(a); //开始排序 sort(a,0,a.length - 1); } /** * 递归调用排序子数组 * @param a * @param lo * @param hi */ private static void sort(Comparable[] a,int lo,int hi){ //改进处:由插入排序替换该行代码if(hi <= lo) return; if(hi <= lo + M){//M取5-15之间的值是比较不错的选择 //插入快速排序的代码如: InsertSort.sort(a,lo,hi);//代码自己实现 return; } //获取一个数组中的取快速切分的基数的下标,使其左边的元素不大于该数, //其右边的元素不小于该数 int j = partition(a,lo,hi); //将左半部分a[lo,j - 1]排序 sort(a,lo,j - 1); //将右半部分a[就+ 1,hi]排序 sort(a,j + 1,hi); } /** * 在数组a的小下标lo和hi范围内获取快速切分的基数的小标,使该基数的左边的 * 元素都不大于该数,而右边的元素都不小于该数 * @param a * @param lo * @param hi * @return 快速切分基数的下标 */ public static int partition(Comparable[] a,int lo,int hi){ //将数组切分为a[lo...i - 1],a[i],a[j + 1,hi] int i = lo,j = hi + 1; //设置左右扫描指针 Comparable v = a[lo]; //切分元素 while(true){ while(less(a[++i],v)){ if(i == hi) break; } while(less(v,a[--j])){ if(j == lo) break; } if(i >= j){ break; } exch(a,i,j); } exch(a,lo,j); //将v = a[i]放入正确的位置 //a[lo...j - 1] <= a[j] <= a[j + 1 ... hi] return j; } }