快速排序思路
以排序数组int numbers = {4,8,2,3,8,9,5,2,3,6,5,8,7,3,6} 为例
整体思路:
典型的分治思想
1、分解
以数组的第一个元素为基准进行排序,大于基准元素的 放在右边,小于基准元素的放在左边,这样就将数组分成了三部分
- 左边数组(小于等于基准数组)
- 基准元素本身
- 右边数组(大于等于基准数组)
2、递归求解
- 分别在第一步基础上 递归地对左边数组以及右边数组排序
3、合并
- 就是将数组合并,由于第一步的所有左边的数组元素 <= 基准元素 <=右边数组,所以不需要进行任何额外的计算数组元素,合并后的数组就是有序的。
快速排序与归并排序的不同点也在此处:
快速排序的排序操作发生在第一步分解时;
归并排序的排序操作发生在第三部合并时;关于归并排序大家可以看看之前的文章。
上面是整体的思路,我们也可以感受到上述三个步骤最困难的就是第一步,我们如何将数组划分为三份,这个比较不好理解,下面我用图的方式来叙述:
下面请看详细的图解:
导出个excel还给我弄个"非会员水印",我穷我忍了。。。
下面咱们撸一下代码
/**
* 快排 递归写法
* @param arr 排序数组
* @param left 起始脚标
* @param right 结束脚标
* @return 结果
*/
public static int[] quickSort(int[] arr,int left,int right){
if(left < right){
//第一步 分解
int mid = partition(arr,left,right);
//第二步 递归求解
quickSort(arr,left,mid-1);
quickSort(arr,mid+1,right);
//第三步 不需要合并 因为数组本身已经有序
}
return arr;
}
/**
* 分解 将数组 分为三份 对应上图中的步骤
*/
public static int partition(int[] arr,int left,int right){
int i = left;
int j = right+1;
//选出一个基准值
int mid = arr[left];
while (true){
//从右向左 找寻比mid小的位置
while (mid < arr[--j] && j >= left );
//从左向右找到比mid大的位置
while (mid > arr[++i] && i < right );
if(i >= j){
break;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
arr[left] = arr[j];
arr[j] = mid;
return j;
}
时间复杂度 T(n) = O(nlogn)。
就地快速排序使用的空间是 O(1) 的 ,是在原数组中排序
而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;
最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况