1 前言
排序在算法中还是很常用的,在这里记录一下快速排序的Java递归实现。
2 实现
public void sort(int []arr) {
quickSort(arr,0,arr.length-1);
}
private void quickSort(int[] arr, int left, int right) {
//边界条件
if(left>=right)
return;
//快排分三步 选出基准、根据基准分区并找出基准的下标、进入分区内部排序
//以左侧的数作为基准
int pivot=arr[left];
//根据基准分区并找出基准的下标
int partition=partition(arr,left,right,pivot);
//进入分区内部排序,调用自身
quickSort(arr, left, partition-1);
quickSort(arr, partition+1, right);
}
private int partition(int[] arr, int left, int right, int pivot) {
//暂存基准所在的下标
int temp=left;
//左指针未超过右指针时继续循环
while(left<=right) {
if(arr[left]>pivot && arr[right]<pivot) {
//左边大于基准且右边小于基准时交换两边的值 并移动下标
swap(arr,left,right);
left++;
right--;
}else if(arr[left]<=pivot) {
//左边还没有找到合适的位置,移动指针
left++;
}else if(arr[right]>=pivot) {
//左边找到了,右边还没找到
right--;
}
}
//基准和right换 或者和left-1换都行
swap(arr, temp, right);
return right;
}
private void swap(int[] arr, int left, int right) {
int temp=arr[right];
arr[right]=arr[left];
arr[left]=temp;
}
Q:为什么基准和right换能行?
A:
1)当左右指针发生重合时,那么此时可能进行过交换或者一次交换都没有进行过,分类讨论
1.1) 如果刚进行过交换,那么此时左右指针两边都是符合要求的,交换后移动指针导致两者重合了,如果设此时左右指针指向的值为x,那么当 x<=pivot时,左指针右移,此时左指针指向了一个比pivot大的数,而左指针-1处的值恰好比pivot小或者相等,那么应该与pivot应该与左指针-1处交换,而左指针-1=右指针,所以直接与右指针交换即可。x>pivot时,右指针左移,为了使基准右侧都是大于或等于pivot的,此时pivot应与right交换。
1.2) 如果一次交换都没有进行过,那么可能是左指针走到了右指针的地方或者右指针走到了左指针的地方,分类讨论
1.2.1) 如果是左指针走到了右指针的地方,那么假设此时指向的值为x,如果x<=pivot,左指针再次移动,跳出循环,说明整个分区的数都比pivot要小或相等,直接与右指针交换即可。如果x>pivot,右指针移动,跳出循环,为了保证基准右侧都是大于或等于pivot的,那么应该与右指针交换。
1.2.2) 如果是右指针走到了左指针的地方,那么此时左指针停住了,它的左侧就小于等于pivot,它当前指向的值要大于pivot,那么右指针会继续向前走,跳出循环,为了保证基准右侧的数都是大于pivot的,基准应该与右指针交换。
2) 如果没有发生过重合,即交换的两个数相邻,两者交换后移动指针直接就跳出循环了,那么此时和左右交换都行,为了保持一致,与右指针交换。
以上是我调试并修改完代码后,看着分组代码最后的交换提出的问题和回答,如果有不同的想法欢迎在评论区留言讨论。快排的思想是一致的,但实现方式各有不同,也有很多的优化,比如基准的选取等,这里就不多说了。