快速排序及改进

        在初等的基本排序算法中,插入排序、归并排序和快速排序是常用的排序算法,实现简单,但很高效,它在空间复杂度和时间复杂度之前取得了最佳的平衡,这也是在现实工作中最常用的原因。
        下面是一些基本排序算法的比较:

                                                            几种常用的排序算法的比较
        快速排序算法是基于“分治算法”的排序算法,其基本思路是:将待排序的集合数组取一个元素叫基数,将其分成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; 
	      }
 }
       

猜你喜欢

转载自flyingdutchman.iteye.com/blog/1863691