快速排序主要思想,选取一个基准,进行一趟排序,调整数组使基准左边的数都小于基准,使基准右边的数都大于基准。然后对于基准划分的两个子数组依次进行递归划分调整。
1.递归版本
public static void quickSort(int[] arr, int lo, int hi){
if(lo>=hi) return;
int q = partition(arr, lo, hi);
quickSort(arr, lo, q-1);
quickSort(arr, q+1, hi);
}
//算法导论版本
// lo---i----j----hi
//lo--i之间存放小于r i---j存放等于r的值 j---hi存放大于r
private static int partition(int[] arr, int lo, int hi) {
//选取基准
int r = arr[hi];
int i = lo-1;
for(int j=lo; j<hi; j++){
//当比基准小,放到左边
if(arr[j]<r){
i++;
swap(arr, i, j);
}
}
swap(arr, i+1, hi);
return i+1;
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
2. 非递归版本
public static void quickSort2(int[] arr, int lo, int hi){
Stack<Integer> s = new Stack<>();
int q = partition(arr, lo, hi);
if(q-1>lo){
s.push(lo);
s.push(q-1);
}
if(q+1<hi){
s.push(q+1);
s.push(hi);
}
while(!s.isEmpty()){
int high = s.pop();
int low = s.pop();
int qq = partition(arr, low, high);
if(qq-1>low){
s.push(low);
s.push(qq-1);
}
if(qq+1<high){
s.push(qq+1);
s.push(high);
}
}
}
非递归主要利用栈来保存需要递归划分的数字坐标
3.partition函数的其他实现方式
选取最小数作为基准r,维护两个指针,分别找到左边第一个比r小的数 右边第一个比r大的数,交换位置。最后调整基准位置。
//算法第四版
private static int partition2(int[] arr, int lo, int hi){
int i = lo;
int j = hi+1;
int r = arr[lo]; //选取arr[lo]作为基准
while(true){
while(arr[++i]<=r) //左边找到第一个比r小的数
if(i==hi) break;
while(arr[--j]>=r) //右边找到第一个比r大的数
if(j==lo) break;
if(i>=j) break;
swap(arr, i, j);
}
swap(arr, lo, j);
return j;
}
private static int partition3(int[] arr, int low, int high){
int pivot = arr[low]; //选第一个元素作为枢纽元
while(low < high)
{
while(low < high && arr[high] >= pivot)high--;
arr[low] = arr[high]; //从后面开始找到第一个小于pivot的元素,放到low位置
while(low < high && arr[low] <= pivot)low++;
arr[high] = arr[low]; //从前面开始找到第一个大于pivot的元素,放到high位置
}
arr[low] = pivot; //最后枢纽元放到low的位置
return low;
}
快速排序时间复杂度平均O(NlogN),当数组基本有序时,时间复杂度变为O(N2);