快速排序及改进

在初等的基本排序算法中,插入排序、归并排序和快速排序是常用的排序算法,实现简单,但很高效,它在空间复杂度和时间复杂度之前取得了最佳的平衡,这也是在现实工作中最常用的原因。
下面是一些基本排序算法的比较:
[img]http://dl.iteye.com/upload/attachment/0084/1628/c3e718d0-00eb-3b5e-a9a2-31996af3a8e0.jpg[/img]
几种常用的排序算法的比较
[b]快速排序算法是基于“分治算法”的排序算法,其基本思路是:将待排序的集合数组取一个元素叫基数,将其分成2个子数组,是基数左边的数都不大于该基数,而右边的数都不小于该基数;然后递归这一分解过程,并对子数组排序,而当所有的子数组都有序时,整个数组也就自然有序了。[/b]
[b]快速排序算法也是一个脆弱的排序算法,在对一个基本有序或已排序的数组做反向排序时,该算法会得到最坏时间复杂度——O(N*N),最好的时间复杂度为NlogN,平均时间复杂度接近于最好的时间复杂度NlogN,其空间复杂度为lgN。[/b]
为了使快速排序获得最好的排序性能,最好在排序之前将之做一次“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;
}
}

[b]快速排序与归并排序相比,一般情况下速度要快于归并排序,但是对于小数组,其排序速度要较插入排序慢一些。[/b]对于在给小数组排序时,快速排序的排序速度要较插入排序慢这一特点,我们可以对快速排序算法做如下改进:

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;
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值