`
leichenlei
  • 浏览: 125356 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

排序(4)快速排序

 
阅读更多

快速排序的原理就是以递归的方式进行划分

 

划分从数组中选择一个数作为枢纽,将数组划分成两部分,一部分小于枢纽,一部分大于枢纽

 

这里列举三种快速排序,第一种是基本的快速排序。第二种优化枢纽选择方式,第三种优化长度较小数组的排序为插入排序。

 

一、选择最右为中枢的快速排序

步骤:

1,选择最右一个为枢纽。

2,划分整个数组。数组变成:[比枢纽小,...,枢纽,比枢纽大,...]

3,比枢纽小的部分递归1,2。

4,比枢纽大的部分递归1,2。

 

 

 

public static int[] data = {3,7,8,0,9,5,4,1,6,2};
	
	/**
	 * 交换位置
	 * @param i
	 * @param j
	 */
	private static void swap(int i, int j){
		int tmp = data[i];
		data[i] = data[j];
		data[j] = tmp;
	}
	/**
	 * 递归
	 * @param leftIndex
	 * @param rightIndex
	 */
	private static void recursion(int leftIndex, int rightIndex){
		if(leftIndex >= rightIndex){
			return;
		}else{
			int pivot = data[rightIndex];	//以最右一个为枢纽
			int pivotIndex = partition(leftIndex, rightIndex, pivot);//以pivot为枢纽进行划分两份,pivotIndex是最终枢纽的位置
			recursion(leftIndex, pivotIndex - 1); //左侧再次递归划分
			recursion(pivotIndex + 1, rightIndex);//右侧再次递归划分
		}
	}
	/**
	 * 
	 * 划分
	 * 以最右一个为枢纽,对“最左” 到“最右-1”进行划分;
	 * 最终划分成两份,左份小于枢纽,右份大于枢纽,枢纽在中间
	 * @param leftIndex
	 * @param rightIndex
	 * @param pivot 枢纽
	 * @return 枢纽索引
	 */
	private static int partition(int leftIndex, int rightIndex, int pivot){
		/** 要划分区是leftIndex到rightIndex - 1。 */
		int left = leftIndex - 1; //要划分区域最左-1
		int right = rightIndex;   //要划分区域最右+1
		while(true){
			while(data[++left] < pivot); //从最左往右遍历,找到一个大的
			while(right > 0 && data[--right] > pivot); //从最右往左遍历,找到一个小的
			if(left >= right){
				break;
			}else{
				swap(left, right);
			}
		}
		swap(left, rightIndex);//枢纽归位
		return left;
	}
	
	public static void main(String[] args) {
		recursion(0, data.length - 1);
		System.out.println(Arrays.toString(data));
	}

 

 

 

 

二、三数据项取中为中枢的快速排序

这种快速排序优化了中枢的选取,但是同时失去了对长度<=3的数组快速排序的能力

步骤:

1,对数组的三个值:第一个值(first)、最后一个值(last)、中间一个值(center),在对应的三个位置上排序,选中间一个为枢纽;最后枢纽和"最后-1"交换位置(即,将枢纽放在倒数第二个位置),数组变成:[三个较小......,枢纽,三个较大......]

2.1,<=3,手动排序

2.2,>3,划分整个数组。数组变成:[三个较小,比枢纽小......,枢纽,比枢纽大......,三个较大]

3,比枢纽小的部分递归1,2。

4,比枢纽大的部分1,2。

 

 

 

public static int[] data = {3,7,8,0,9,5,4,1,6,2};
	
	/**
	 * 交换位置
	 * @param i
	 * @param j
	 */
	private static void swap(int i, int j){
		int tmp = data[i];
		data[i] = data[j];
		data[j] = tmp;
	}
	
	
	/**
	 * 由于长度小于等于3无法进行快速排序。
	 * 当长度小于等于3时直接手动排序,推出递归
	 * 对<=3的数组手动排序
	 * @param left
	 * @param right
	 */
	private static void manualSort(int left, int right){
		int size = right - left + 1;
		if(size <= 1){
			return;
		}
		if(size == 2){
			if(data[left] > data[right]){
				swap(left, right);
			}
		}else{
			if(data[left] > data[right - 1]){
				swap(left, right - 1);
			}
			if(data[left] > data[right]){
				swap(left, right);
			}
			if(data[right - 1] > data[right]){
				swap(right - 1, right);
			}
		}
	}
	/**
	 * 选择枢纽
	 * @param left
	 * @param right
	 * @return
	 */
	private static int medianOf3(int left, int right){
		/** 从数组的开始,结束,和中间,对着在对应的三个位置上排序,枢纽放在right-1处,并返回枢纽 */
		int center = (right + left) / 2;
		if(data[left] > data[center]){
			swap(left, center);
		}
		if(data[left] > data[right]){
			swap(left, right);
		}
		if(data[center] > data[right]){
			swap(center, right);
		}
		/** 最后把居中的那个放在right-1处,作为枢纽 */
		swap(center, right - 1);//
		return data[right - 1];
	}
	/**
	 * 
	 * 划分
	 * @param leftIndex
	 * @param rightIndex
	 * @param pivot 
	 * @return 
	 */
	private static int partition(int leftIndex, int rightIndex, int pivot){
		/** 
		 * leftIndex经过medianOf3已经是小的了
		 * rightIndex经过medianOf3已经是大的了
		 * rightIndex - 1 是枢纽
		 * 要划分的区域是leftIndex+1到rightIndex-2。
		 */
		int left = leftIndex; 		  //要划分区域最左-1
		int right = rightIndex - 1;   //要划分区域最右+1
		while(true){
			while(data[++left] < pivot); //从最左往右遍历,找到一个大的
			while(data[--right] > pivot); //从最右往左遍历,找到一个小的
			if(left >= right){
				break;
			}else{
				swap(left, right);
			}
		}
		swap(left, rightIndex - 1);//枢纽归位
		return left; //返回枢纽位置
	}
	
	/**
	 * 递归
	 * @param leftIndex
	 * @param rightIndex
	 */
	private static void recursion(int leftIndex, int rightIndex){
		if(rightIndex - leftIndex + 1 <= 3){
			manualSort(leftIndex, rightIndex);//手动排序
		}else{
			int pivot = medianOf3(leftIndex, rightIndex);//选择枢纽 
			int pivotIndex = partition(leftIndex, rightIndex, pivot);//以pivot为枢纽进行划分两份,pivotIndex是最终枢纽的位置
			recursion(leftIndex, pivotIndex - 1); //左侧再次递归划分
			recursion(pivotIndex + 1, rightIndex);//右侧再次递归划分
		}
	}
	
	public static void main(String[] args) {
		recursion(0, data.length - 1);
		System.out.println(Arrays.toString(data));
	}

 

 

三、较小数组改用插入排序的快速排序

 将二中的手动排序改成插入排序,条件改成<10

public static int[] data = {3,7,8,0,9,5,4,1,6,2};
	
	/**
	 * 交换位置
	 * @param i
	 * @param j
	 */
	private static void swap(int i, int j){
		int tmp = data[i];
		data[i] = data[j];
		data[j] = tmp;
	}
	/**
	 * 选择枢纽
	 * @param left
	 * @param right
	 * @return
	 */
	private static int medianOf3(int left, int right){
		/** 从数组的开始,结束,和中间,对着在对应的三个位置上排序,枢纽放在right-1处,并返回枢纽 */
		int center = (right + left) / 2;
		if(data[left] > data[center]){
			swap(left, center);
		}
		if(data[left] > data[right]){
			swap(left, right);
		}
		if(data[center] > data[right]){
			swap(center, right);
		}
		/** 最后把居中的那个放在right-1处,作为枢纽 */
		swap(center, right - 1);//
		return data[right - 1];
	}
	/**
	 * 插入排序
	 * @param left
	 * @param right
	 */
	private static void insertionSort(int left, int right){
		for(int i = left+1; i <= right; i++){  
	        int temp = data[i];  
	        int j = i - 1;  
	        while(j >= left && data[j] >= temp){  
	            data[j + 1] = data[j];  
	            j--;  
	        }  
	        data[j + 1] = temp;  
	    }  
	}
	/**
	 * 
	 * 划分
	 * @param leftIndex
	 * @param rightIndex
	 * @param pivot 
	 * @return 
	 */
	private static int partition(int leftIndex, int rightIndex, int pivot){
		/** 
		 * leftIndex经过medianOf3已经是小的了
		 * rightIndex经过medianOf3已经是大的了
		 * rightIndex - 1 是枢纽
		 * 要划分的区域是leftIndex+1到rightIndex-2。
		 */
		int left = leftIndex; 		  //要划分区域最左-1
		int right = rightIndex - 1;   //要划分区域最右+1
		while(true){
			while(data[++left] < pivot); //从最左往右遍历,找到一个大的
			while(data[--right] > pivot); //从最右往左遍历,找到一个小的
			if(left >= right){
				break;
			}else{
				swap(left, right);
			}
		}
		swap(left, rightIndex - 1);//枢纽归位
		return left; //返回枢纽位置
	}
	
	/**
	 * 递归
	 * @param leftIndex
	 * @param rightIndex
	 */
	private static void recursion(int leftIndex, int rightIndex){
		if(rightIndex - leftIndex + 1 < 10){
			insertionSort(leftIndex, rightIndex);//插入排序
		}else{
			int pivot = medianOf3(leftIndex, rightIndex);//选择枢纽 
			int pivotIndex = partition(leftIndex, rightIndex, pivot);//以pivot为枢纽进行划分两份,pivotIndex是最终枢纽的位置
			recursion(leftIndex, pivotIndex - 1); //左侧再次递归划分
			recursion(pivotIndex + 1, rightIndex);//右侧再次递归划分
		}
	}
	
	public static void main(String[] args) {
		recursion(0, data.length - 1);
		System.out.println(Arrays.toString(data));
	}

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics