经典排序算法之快排:原理及java实现

快排 java实现

一、快排原理(总结至百度百科)
原理:快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
具体实现如下
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大或者等于它的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,即分界值key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换; (即交换后A[j]为分界值key)
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;(即交换后A[i]为分界值key)
5)重复第3、4步,直到i=j; (注意,无论最后终止时,是停在(3)还是(4),最终i=j,A[i]=A[j]=key,则完成了以key值为分界,左边都比key小,右边都大于或等于key)。
举例说明如下:
假设一开始序列{x}是:5,3,7,6,1,1,0,2,9,10,8。
此时,key=5,i=0,j=10。
从后往前找j- -,第一个比5小的数是x[j=7]=2,则x[j=7]与x[i=0]互换,因此序列为:2,3,7,6,1,1,0,5,9,10,8。此时i=0,j=7,x[j=7]=key;
从前往后找i++,第一个比5大的数是x[i=2]=7,则x[i=2]和x[j=7]互换,因此序列为:2,3,5,6,1,1,0,7,9,10,8。此时,i=2,j=7,x[i=2]=key;
从第7位往前找,j- -,第一个比5小的数是x6=0,则x[j=6]和x[i=2]互换,序列为:2,3,0,6,1,1,5,7,9,10,8。此时,i=2,j=6,x[j=6]=key;
从第2位往后找,i++,第一个比5大的数是x3=6,则x[i=3]和x[j=6]互换,因此:2,3,0,5,1,1,6,7,9,10,8。此时,i=3,j=6,x[i=3]=key;
从第6位往前找,j- -,第一个比5小的数是x5=1,则x[j=5]和x[i=3]互换,因此:2,3,0,1,1,5,6,7,9,10,8。此时,i=3,j=5,x[j=5]=key;
从第3位往后找,i++, 直到第5位才有等于5的数,这时,i=j=5,x[i=5]=x[j=5]=key, key成为一条分界线,2,3,0,1,1,5,6,7,9,10,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。

可以对上述实现进行改进减少交换次数:
取头部第一个数为枢纽元key,两个指针,分别从数组的首尾开始向中间运动,尾部指针先运动,尾部指针指到的数若小于枢纽元则停下来;
当尾部指针停下来的时候,头部指针运动,头部指针指到的数若大于枢纽元则停下来,此时,交换头尾指针指向的数,然后重复上述运动。直到头尾指针相遇,运动结束。
由于头部指针运动时并没有修改等于枢纽元的数的位置,即枢纽元位置不变,将头尾指针指向的数与枢纽元的值交换,则此时枢纽元位于分界线位置。(由于先尾指针运动,后头指针运动,所以最终和枢纽元交换的数只能是一个小于或者等于枢纽元值的数)
以上即一趟快速排序过程,之后,在从分界线位置将数组分为两部分,两部分分别仿照上述过程进行排序过程。
注意:一定是尾部指针先运动,头部指针后运动,否则排序会出现错误。

二、代码
第一种原理:

package array;

public class QuickSort {
	public static int[] qsort(int arr[],int start,int end) {        
	    int pivot = arr[start];        
	    int i = start;        
	    int j = end;        
	    while (i<j) {            
	        while ((i<j)&&(arr[j]>=pivot)) {                
	            j--;            
	        }   
			if (i<j) { //这里的if语句是减少i=j时的那一次交换 
				 int temp = arr[i]; 
				 arr[i] = arr[j]; 
				 arr[j]= temp; 
			}
			
	        while ((i<j)&&(arr[i]<pivot)) {                
	            i++;            
	        }  
	        if (i<j) { //这里的if语句是减少i=j时的那一次交换 
				 int temp = arr[i]; 
				 arr[i] = arr[j]; 
				 arr[j]= temp; 
				 }
			
	    }      
	    
	    if(i-1>start) {
	    	arr=qsort(arr,start,i-1);
	    	}
	    
	    if(end>j+1) {
	    	arr=qsort(arr,j+1,end);
	    	}
	    
	    return arr;    
	}    	 
	public static void main(String[] args) {        
	    int arr[] = new int[]{3,433,3,7,9,122,34,4,4656,34,34,4656,5,3,6,7,1,8,9,343,57,765,23,1,23,21};        
	    int len = arr.length-1;        
	    arr=qsort(arr,0,len);        
	    for (int i:arr) {            
	        System.out.print(i+" ");        
	    }    
}

}

改进后的原理

代码如下:

//实参:如果是基本类型或者String,则实参不会变(传的是值); 如果是对象集合或者数组,则实参会改变(传的是引用)
	//所以方法不需要return一个数组,传入的数组参数会随着排序而改变。
	public static void qsort2(int arr[],int start,int end) {
		
		if(start>=end) {
			return;
		}
		
		int i =start;
		int j =end;
		int key = arr[i];
		
		//数组中比key小的放在左边,比key大或等于的放在右边
		while(i<j) {
			//一定是j--在前,i++在后,否则会出现错误
			while((i<j)&&(arr[j]>=key)){//这里带等号是保证大于等于key的项不需要移动,在右侧就好
				j--;
			}
			while((i<j)&&(arr[i]<=key)) {
				//这里带等号是为了保证第0项不移动,后续方便arr[i]与arr[start]=key交换
				i++;
			}
			
			if(i<j) {
				int temp =arr[i];
				arr[i]=arr[j];
				arr[j]=temp;
			}
		}
		
		//因为是先j--,再i++,所以,此时arr[i]<=a[start]
		//最后将基准key与i和j相等位置的数字交换
		arr[start] = arr[i];
		arr[i]=key;//交换使得key分界线回到中间
		
		qsort2(arr,start,i-1);//递归调用左半数组
		qsort2(arr,i+1,end); //递归调用右半数组
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值