快速排序(Java)

169 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

原理

  • 选取一个基准数,找到这个基准数的位置;基准数将数组分割成2个区域的子数组;
  • 分别对2个区域的子数组重复步骤一,直到不能分割为止;

  什么基准数?怎么找到这个基准数的位置?这个基准数是怎么分割数组的?下面会对这些问题以图解的方式来讲解,快速排序其实非常简单。

基准数的选取

  可以任意选择数组的元素作为基准数;一般来讲,取排序区域的第一个数作为基准数;

找基准数位置

升序为例,先看下图:

  假设36,在排序后的数组中位置如图所示;那么在36前面的数字一定是小于或者等于36,在36之后的数字一定是大于或者等于36; 利用这个特性找36在数组中的正确位置时遍历数组将大于36的数字放在36后面,将小于36的数字放在36前面;当遍历完整个数组时,就能够确定36的位置了;

  代码实现使用双指针: i=0,j=len-1;从后往前找比36小的数字,找到后与36交换位置;再从前往后找比36大的数字找到后交换位置,当指针碰撞时,说明遍历完了整个数组;

数组分割

  在找到基准数的正确位置之后,数组被分成了两部分;虽然这两部分区域中的数据仍然是无序的,但是将这两部分区域当成2个整体来看,这2个整体是有序的;

  乱序1的区域中所有数字 小于或者等于 乱序2区域中的所有数字;正是整个特性可以将乱序1和乱序2区域的数字分别排序;当乱序1和乱序2区域排序完成之后,整个数组就排序完成;乱序1和乱序2的排序过程又与之前一样,因此这2部分区域的排序直接分别重复上面的步骤即可;

  快速排序之所以快,也就是因为这个原因;每遍历完一个区域之后,不仅可以得到一个元素的有效位置,而且还将区域分割成了2个整体有序的区域,2个区域排序时互不干扰,这就大大减少了比较次数;

代码:

	public void sort(int a[],int left,int right) {
		
		
		if(left < right) {//递归出口条件判断
			int x = a[left];		
			int i=left;
			int j = right;
 
//整个循环目的:用基准数x,将数组分为2个部分(准确来讲是3部分->【左数组】,x,【右数组】)
			while(i<j) {
				/**从后往前比较*/
				while(i < j && a[j] <= x)
					j--;
				if(i < j) {
					a[i]=a[j];
                                        a[j]=x;
					i++;
				}
				
				/**从前往后比较*/
				while(i<j && a[i] > x) 
					i++;
				if(i<j) {
					a[j]=a[i];
                                        a[i]=x;
					j--;				
				}
			}				
			sort(a,left,i-1);//左边数组
			sort(a,i+1,right);//右边数组
 
			}		
	}