1.1 原理
- 从待排序区间选择一个数,作为基准值(pivot);
- Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
- 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度等于1,代表已经有序,或者小区间的长度等于0,代表没有数据
1.2 快速排序图示
1.3 代码示例(原理-partition)
1.3.1 Hoare法
//hoare版本
public int partitionHoare(int[] array,int lowIndex,int highIndex){
int leftIndex = lowIndex;
int rightIndex = highIndex;
int key = array[lowIndex];
//基准值选取 左边值
while(leftIndex<rightIndex){
while(leftIndex<rightIndex && array[rightIndex]>=key){
rightIndex--;
}
//出循环说明rightIndex 遇到比key小的值了
while(leftIndex<rightIndex && array[leftIndex]<=key){
leftIndex++;
}
//出循环说明leftIndex 遇到比key大的值了
//交换这两个位置的值
swap(array,leftIndex,rightIndex);
}
swap(array,lowIndex,leftIndex);
return leftIndex;
}
public void quickSortInternal_Hoare(int[] array,int lowIndex,int highIndex){
int size = highIndex-lowIndex+1;
if(size<=1){
return;
}
//选取一个基准值:选取最左边的数
//执行partition,小的放左,大的放右
//经过partition之后选出来的最终值
int keyIndex = partitionHoare(array,lowIndex,highIndex);
//分别对左右区间进行相同的处理方法
quickSortInternal_Hoare(array,lowIndex,keyIndex-1);
quickSortInternal_Hoare(array,keyIndex+1,highIndex);
}
public void swap(int[] array,int index1,int index2){
int tmp = array[index1];
array[index1] = array[index2];
array[index2] = tmp;
}
public void quickSort_Hoare(int[] array){
quickSortInternal_Hoare(array,0,array.length-1);
}
1.3.2 挖坑法
基本思路和Hoare法一致,只是不再进行交换,而是进行赋值(填坑+挖坑)
//挖坑法
public void quickSortInternal_Dig(int[] array,int lowIndex,int highIndex) {
//由于是闭区间,所以区间个数需要加一
int size = highIndex-lowIndex+1;
if(size<=1) {
return;
}
//选取其中一个数(选最左边的)
//执行partition,小的放左,大的放右
//经过partiton之后选出来的最终值
int keyIndex = partitionDig(array,lowIndex,highIndex);
//分别对左右区间进行相同的处理方法——递归方法
quickSortInternal_Dig(array,lowIndex,keyIndex-1);
quickSortInternal_Dig(array, keyIndex+1, highIndex);
}
public int partitionDig(int[] array,int lowIndex,int highIndex) {
int leftIndex = lowIndex;
int rightIndex = highIndex;
int key = array[lowIndex];
//选择了最左边,从右边先走
while(leftIndex<rightIndex) {
while(leftIndex<rightIndex&&array[rightIndex]>=key) {
rightIndex--;
}
//说明rightIndex遇到比key小的值了
array[leftIndex] = array[rightIndex];
while(leftIndex<rightIndex&&array[leftIndex]<=key) {
leftIndex++;
}
//说明leftIndex遇到比key大的值了
array[rightIndex] = array[leftIndex];
}
//填坑
array[leftIndex] = key;
//到这
return leftIndex;
}
public void quickSort_Dig(int[] array) {
quickSortInternal_Dig(array,0,array.length-1);
}
1.3.3 前后指针法
//前后指针法
public int partitionPoint(int[] array,int lowIndex,int highIndex) {
// int front,rear;
// front = rear = lowIndex+1;
// //选择了最左边,从右边先走
// while(rear<=highIndex) {
// if(array[rear]<array[lowIndex]) {
// swap(array,rear,front);
// front++;
// }
// rear++;
// }
// swap(array,lowIndex,front-1);
// //到这
// return front-1;
int front = lowIndex+1;
for(int rear = lowIndex+1;rear<=highIndex;rear++) {
if(array[rear]<array[lowIndex]) {
swap(array,rear,front);
front++;
}
}
swap(array,lowIndex,front-1);
return front-1;
}
//前后指针
public void quickSortInternal_Point(int[] array,int lowIndex,int highIndex) {
//由于是闭区间,所以区间个数需要加一
int size = highIndex-lowIndex+1;
if(size<=1) {
return;
}
//选取其中一个数(选最左边的)
//执行partition,小的放左,大的放右
//经过partiton之后选出来的最终值
int keyIndex = partitionPoint(array,lowIndex,highIndex);
//分别对左右区间进行相同的处理方法——递归方法
quickSortInternal_Point(array,lowIndex,keyIndex-1);
quickSortInternal_Point(array, keyIndex+1, highIndex);
}
public void quickSort_Point(int[] array) {
quickSortInternal_Point(array,0,array.length-1);
}
1.4 性能分析
-
时间复杂度:
- 最好:O(n*log(n))
- 平均:O(n*log(n))
- 最坏:O(n^2)
-
空间复杂度:
- 最好:O(log(n))
- 平均:O(log(n))
- 最坏:O(n)
1.5 基准值的选择及优化
-
在待排序区间选择一个基准值
- 选择边上(左或者右)
- 随机选择
- 几数取中(三数取中)
-
做partition,使得小的数在左,大的数在右(小细节优化)
-
数量比较少,不是最快(当区间的个数低于某个阈值时(16),使用插排)
-
相等的值做特殊处理