快速排序(QuickSort)是一种常用的排序算法,属于交换排序的一种。它的基本思想是通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字都比另一部分记录的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
快速排序的基本步骤如下:
选择基准元素: 从待排序序列中选择一个元素作为基准元素。通常选择序列的第一个元素,但也可以随机选择或者选择中间元素。
分割操作: 将比基准元素小的元素移到基准元素的左边,将比基准元素大的元素移到基准元素的右边。基准元素在这一过程中已经找到了最终的位置,它左边的元素都比它小,右边的元素都比它大。
递归排序: 对基准元素左右两边的子序列分别进行快速排序,重复以上步骤,直到整个序列有序。
public static void swap(int[] nums,int left,int right){
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
public static int partition(int[] nums,int left,int right){
int i = left;
int j = right;
while(i < j){
while(i < j && nums[j] >= nums[left]){
j--;
}
while(i < j && nums[i] <= nums[left]){
i++;
}
swap(nums,i,j);
}
swap(nums,left,i);
return i;
}
public static void quicksort(int[] nums,int left,int right){
if(left >= right){
return ;
}
int pivot = partition(nums,left,right);
quicksort(nums,left,pivot-1);
quicksort(nums,pivot+1,right);
}
时间复杂度:O(n^log n),在平均情况下,哨兵划分的递归层数为 log n ,每层中的总循环数为 n ,总体使用 O(n^log n) 时间。在最差情况下,每轮哨兵划分操作都将长度为 n 的数组划分为长度为 0 和 n−1 的两个子数组,此时递归层数达到 n 层,每层中的循环数为 n ,总体使用 O(n^2) 时间。
空间复杂度:O(n),在输入数组完全倒序的情况下,达到最差递归深度 n ,使用 O(n) 栈帧空间。排序操作是在原数组上进行的,未借助额外数组。
非稳定排序: 在partition方法的最后一步,基准数可能会被交换至相等元素的右侧。
为了进一步改进,我们可以在数组中选取三个候选元素(通常为数组的首、尾、中点元素),并将这三个候选元素的中位数作为基准数。这样一来,基准数“既不太小也不太大”的概率将大幅提升。当然,我们还可以选取更多候选元素,以进一步提高算法的稳健性。采用这种方法后,时间复杂度劣化至 O(n^2) 的概率大大降低。
public static void swap(int[] nums,int left,int right){
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
public static int medianThree(int[] nums,int left,int mid,int right){
if((nums[left] < nums[mid])^(nums[left] < nums[right])){
return left;
}else if((nums[right] < nums[mid])^(nums[right] < nums[left])){
return right;
}else{
return mid;
}
}
public static int partition(int[] nums,int left,int right){
int pivot = medianThree(nums,left,(left+right)/2,right);
swap(nums,left,pivot);
int i = left;
int j = right;
while(i < j){
while(i < j && nums[j] >= nums[left]){
j--;
}
while(i < j && nums[i] <= nums[left]){
i++;
}
swap(nums,i,j);
}
swap(nums,left,i);
return i;
}
public static void quicksort(int[] nums,int left,int right){
if(left >= right){
return ;
}
int pivot = partition(nums,left,right);
quicksort(nums,left,pivot-1);
quicksort(nums,pivot+1,right);
}
参考:11.5 快速排序 - Hello 算法 (hello-algo.com)