本文已参与「新人创作礼」活动,一起开启掘金创作之路。
原理
- 选取一个基准数,找到这个基准数的位置;基准数将数组分割成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);//右边数组
}
}