快速排序

1. 划分

        划分是快速排序的根本机制。划分就是 将一个数组按照指定的值,划分为两部分。左侧小于指定值,右侧大于指定值。

        思路:定义一个枢纽值,将指针定位到数组的两侧,相向移动。左侧指针遇到 应在右侧部分的项(即:数据项的值大于枢纽值)时停止,右侧指针遇到 应在左侧部分的项(即:数据项的值小于枢纽值)时停止,交换两个项的位置,重复这一过程,直至两个指针交错而过。

        实现:

                 

package com.test;

/**
 * 划分
 * @author xurongsheng
 * @date 2017年4月12日 下午3:29:00
 *
 */
public class Partition {

	private long[] target;//待排序数据
	
	public Partition(){
		
	}
	
	public Partition(int maxLength){
		target = new long[maxLength];
	}
	
	public void setTargetValue(int index,long value){
		target[index] = value;
	}
	
	public long getTargetValue(int index){
		return target[index];
	}
	
	public void display(){
		System.out.print("A=");
		for (int i = 0; i < target.length; i++) {
			System.out.print(target[i]+" ");
		}
		System.out.println(" ");
	}
	
	/**
	 * 划分
	 * 比较次数:N+1 或 N+2
	 * 交换次数:N/2
	 * 时间复杂度: O(N)
	 * @author xurongsheng
	 * @date 2017年4月12日 下午3:30:59
	 * @param left 数组的最左侧
	 * @param right 数组的最右侧
	 * @param pivot 枢纽值
	 */
	public int partition(int left,int right,long pivot){
		int leftPtr = left - 1; //左侧指针
		int rightPtr = right + 1; //右侧指针
		while(true){
			while(leftPtr < right && target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
  
  <枢纽值的项 ; } while(rightptr>
   
    left && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到  值>枢纽值的项
				;
			}
			if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
				break;
			}else{
				swap(leftPtr,rightPtr);
			}
		}
		return leftPtr;
	}
	
	/**
	 * 交换两个位置上的值
	 * @author xurongsheng
	 * @date 2017年4月13日 下午2:50:29
	 * @param left
	 * @param right
	 */
	public void swap(int left,int right){
		long temp;
		temp = target[left];
		target[left] = target[right];
		target[right] = temp;
	}
	
	public static void main(String[] args) {
		int maxSize = 10;
		Partition p = new Partition(maxSize);
		
		for (int i = 0; i < maxSize; i++) {
			long n = (long) (Math.random()*199);
			p.setTargetValue(i,n);
		}
		p.display();
		
		long pivot = 99;
		System.out.print("枢纽值:"+pivot);
		
		int pivotIndex = p.partition(0, p.target.length-1, pivot);
		System.out.println(",枢纽所在位置:"+pivotIndex);
		p.display();
		
	}
	
}

  
  

        效率:

                 划分的时间复杂度为O(N)  ,对于每一次的划分,都会有 N+1 或者 N+2次的比较。最多会有 N/2次的交换。


2. 快速排序

        思路:将数组的最后一项作为枢纽值,进行划分,划分完后的 左右两部分,分别递归执行 划分。最终将实现排序

        实现:

                 
package com.test;

/**
 * 快速排序
 * @author xurongsheng
 * @date 2017年4月13日 下午2:58:27
 *
 */
public class QuickSort {

private long[] target;//待排序数据
	
	public QuickSort(){
		
	}
	
	public QuickSort(int maxLength){
		target = new long[maxLength];
	}
	
	public void setTargetValue(int index,long value){
		target[index] = value;
	}
	
	public long getTargetValue(int index){
		return target[index];
	}
	
	public void display(){
		System.out.print("A=");
		for (int i = 0; i < target.length; i++) {
			System.out.print(target[i]+" ");
		}
		System.out.println(" ");
	}
	
	/**
	 * 快速排序
	 * 时间复杂度
	 * @author xurongsheng
	 * @date 2017年4月13日 下午3:13:13
	 */
	public void quickSort(){
		recQuickSort(0,target.length-1);
	}
	
	public void recQuickSort(int left,int right){
		if(right-left <= 0){
			return;
		}else{
			long pivot = target[right]; //最后一项作为 枢纽值
			int pivotIndex = partition(left,right,pivot);// 划分并返回枢纽值的最终位置
			recQuickSort(left,pivotIndex-1);//将左侧 递归划分
			recQuickSort(pivotIndex+1,right);//将右侧 递归划分
		}
	}
	
	public int partition(int left,int right,long pivot){
		int leftPtr = left - 1; //左侧指针
		int rightPtr = right; //右侧指针[由于将最后一项作为了枢纽值,所以指针无需再右移]
		while(true){
			while(target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
   
   <枢纽值的项 ; } while(rightptr>
    
     0 && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到  值>枢纽值的项
				;
			}
			if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
				break;
			}else{
				swap(leftPtr,rightPtr);
			}
		}
		swap(leftPtr,right); //将枢纽值与 划分后的右侧数组首项交换位置
		return leftPtr;
	}
	
	/**
	 * 交换两个位置上的值
	 * @author xurongsheng
	 * @date 2017年4月13日 下午2:50:29
	 * @param left
	 * @param right
	 */
	public void swap(int left,int right){
		long temp;
		temp = target[left];
		target[left] = target[right];
		target[right] = temp;
	}
	
	public static void main(String[] args) {
		int maxSize = 16;
		QuickSort qs = new QuickSort(maxSize);
		
		for (int i = 0; i < maxSize; i++) {
			long n = (long) (Math.random()*99);
			qs.setTargetValue(i,n);
		}
		qs.display();
		qs.quickSort();
		qs.display();
		
	}
}

   
   
        效率:
                 快速排序的时间复杂度依赖于 枢纽的选择。理想状态下,枢纽的值应当是被排序数据项的中间值。
                 快速排序  在最好的情况下的时间复杂度为 O(N*logN),最坏情况下的时间复杂度为 O(N²),平均的时间复杂度为 O(N*logN)

         另一种实现:

                 

package com.test;

/**
 * 快速排序[切割版]
 * @author xurongsheng
 * @date 2017年4月13日 下午4:22:09
 *
 */
public class quickSort2 {
private long[] target;//待排序数据
	
	public quickSort2(){
		
	}
	
	public quickSort2(int maxLength){
		target = new long[maxLength];
	}
	
	public void setTargetValue(int index,long value){
		target[index] = value;
	}
	
	public long getTargetValue(int index){
		return target[index];
	}
	
	public void display(){
		System.out.print("A=");
		for (int i = 0; i < target.length; i++) {
			System.out.print(target[i]+" ");
		}
		System.out.println(" ");
	}
	
	/**
	 * 快速排序
	 * 时间复杂度
	 * @author xurongsheng
	 * @date 2017年4月13日 下午3:13:13
	 */
	public void quickSort(){
		recQuickSort(0,target.length-1);
	}
	
	public void recQuickSort(int left,int right){
		int size = right-left+1;
		if(size < 10){ //数据项小于10个时,执行插入排序
			insertionSort(left, right);
		}else{ 
			long pivot = medianOf3(left, right); //最左项、中间项、最右项,取中值作为 枢纽值
			int pivotIndex = partition(left,right,pivot);// 划分并返回枢纽值的最终位置
			recQuickSort(left,pivotIndex-1);//将左侧 递归划分
			recQuickSort(pivotIndex+1,right);//将右侧 递归划分
		}
	}
	
	public long medianOf3(int left,int right){
		//将数组的最左侧的项、中间项、最右侧的项,排序
		int center = (left + right) / 2;
		if(target[left] > target[center]){
			swap(left,center);
		}
		if(target[left] > target[right]){
			swap(left,right);
		}
		if(target[center] > target[right]){
			swap(center,right);
		}
		//中间项即为 枢纽值,将至置换到右侧
		swap(center,right-1);
		return target[right-1];
	}
	
	/**
	 * 插入排序
	 * @author xurongsheng
	 * @date 2017年4月13日 下午4:41:08
	 * @param left
	 * @param right
	 */
	public void insertionSort(int left,int right){
		int in,out;
		for(out = left+1; out<=right; out++){
			long temp = target[out];
			in = out;
			while(in>left && target[in-1] >= temp){
				target[in] = target[in-1];
				--in;
			}
			target[in] = temp;
		}
	}
	
	public int partition(int left,int right,long pivot){
		int leftPtr = left - 1; //左侧指针
		int rightPtr = right; //右侧指针[由于将最后一项作为了枢纽值,所以指针无需再右移]
		while(true){
			while(target[++leftPtr] < pivot){ //左侧指针向右移动,直至遇到 值
  
  <枢纽值的项 ; } while(rightptr>
   
    0 && target[--rightPtr] > pivot){ //右侧指针向左移动,直至遇到  值>枢纽值的项
				;
			}
			if(leftPtr >= rightPtr){ //左侧指针 越过了 右侧指针,结束
				break;
			}else{
				swap(leftPtr,rightPtr);
			}
		}
		swap(leftPtr,right); //将枢纽值与 划分后的右侧数组首项交换位置
		return leftPtr;
	}
	
	/**
	 * 交换两个位置上的值
	 * @author xurongsheng
	 * @date 2017年4月13日 下午2:50:29
	 * @param left
	 * @param right
	 */
	public void swap(int left,int right){
		long temp;
		temp = target[left];
		target[left] = target[right];
		target[right] = temp;
	}
	
	public static void main(String[] args) {
		int maxSize = 16;
		quickSort2 qs = new quickSort2(maxSize);
		
		for (int i = 0; i < maxSize; i++) {
			long n = (long) (Math.random()*99);
			qs.setTargetValue(i,n);
		}
		qs.display();
		qs.quickSort();
		qs.display();
		
	}
}

  
  

          说明:由于 枢纽的值对于排序的效率影响很大,为了避免出现最右侧的值偏小或者偏大的情况,可以去数组的第一个、最后一个、中间一个,从中选出中间值的项作为枢纽。方法medianOf3 就是选取中间值。这又带来另一个问题,三数据项取中的办法,首先要求数组的项数起码要大于3. 一个可行的办法是 小于3项的 手动排序. 3 就作为了 切割点,将排序方法分为了 手动排序和快速排序两种。 对于小于切割点的小数据,还可以用插入排序来处理。这样效率更高些,切割点也可以设置的高些,建议是9 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值