快速排序基本思想
快速排序也是一种交换排序,采用分治法,每一轮先找一个基准元素,然后把比基准元素大的移动到它的一边,比基准元素小的移动到另一边,从而把数列拆分成两个部分,再对这两个部分的子数列采用相同的操作,直到整个数列不能继续拆解,这样整个数列就是有序的了。快速排序的每一轮下来都会把基准元素放到有序的位置上。
性能分析
- 稳定性:不稳定
- 时间复杂度:快排每一次的比较和交换都需要把整个数组遍历一遍,时间复杂度时O(n),这样的遍历一共有logn轮,所以总体的时间复杂度是O(nlogn).
基准元素的选择
- 一般选取数列的第一个元素为基准元素
- 随机选取
元素的交换
双边循环法
过程:
原始数列:
首先选取基准元素pivot,并且设置两个指针left,right,指向数列的最左和最右两个元素,如下:
接下来进行第一轮循环,从right开始,让指针所指向的元素和基准元素pivot作比较,若大于或等于pivot,则指针向左移动;小于pivot则right指针停止移动,切换到left指针。
在当前数列中,1<4,所以right直接停止移动,换到left指针,进行下一步行动。left指针移动,让其所指向的元素和pivot作比较,小于或等于pivot,则指针向右移动;大于则停止移动。
由于left指针开始时指向的时基准元素,所以相等,所以left向右移动1位。如下:
由于7>4,left指针停下来,这是交换left和right指针所指向的元素。
接下来,进行第2次循环,重新切换到right指针,向左移动。right移动到8,8>4,继续左移。由于2<4,停在2的位置。
按照这个思路,后续的步骤如下:
单边循环法
原始序列如下:
确定基准元素pivot,同时设置一个mark指针指向数列的起始位置,这个mark指针代表小于基准元素的区域边界
接下来,从基准元素的下一个位置开始遍历数组。
如果遍历到的元素大于pivot,就继续往右遍历。
如果遍历到的元素小于基准元素,则需要做两件事,第一:把mark指针右移1位,因为小于pivot的 区域边界增大了1;第二:让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域。
首先遍历到元素7,7>4,继续遍历。
接下来遍历到的是元素3,3<4,所以mark右移一位。
随后,让元素3和mark指针所在位置的元素交换位置,因为3归属于小于pivot的区域。
按照这个思路,后续步骤如下:
代码实现
双边循环法的快排:
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 递归结束条件:startIndex大等于endIndex的时候
if (startIndex >= endIndex) {
return;
}
// 得到基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 根据基准元素,分成两部分递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
/**
* 分治(双边循环法)
* @param arr 待交换的数组
* @param startIndex 起始下标
* @param endIndex 结束下标
*/
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素(也可以选择随机位置)
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while( left != right) {
//控制right指针比较并左移
while(left<right && arr[right] > pivot){
right--;
}
//控制left指针比较并右移
while( left<right && arr[left] <= pivot) {
left++;
}
//交换left和right指向的元素
if(left<right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指针重合点交换
arr[startIndex] = arr[left];
arr[left] = pivot;
return left;
}
单边循环法的快排:
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 递归结束条件:startIndex大等于endIndex的时候
if (startIndex >= endIndex) {
return;
}
// 得到基准元素位置
int pivotIndex = partitionV2(arr, startIndex, endIndex);
// 根据基准元素,分成两部分递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
/**
* 分治(单边循环法)
* @param arr 待交换的数组
* @param startIndex 起始下标
* @param endIndex 结束下标
*/
private static int partitionV2(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素(也可以选择随机位置)
int pivot = arr[startIndex];
int mark = startIndex;
for(int i=startIndex+1; i<=endIndex; i++){
if(arr[i]<pivot){
mark ++;
int p = arr[mark];
arr[mark] = arr[i];
arr[i] = p;
}
}
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。