描述
快速排序(有时称为分区交换排序)是一种高效的排序算法。由英国计算机科学家Tony Hoare于1959年开发并于1961年发表,它在现在仍然是一种常用的排序算法。如果实现方法恰当,它可以比主要竞争对手(归并排序和堆排序)快两到三倍。
其核心的思路是取第一个元素(或者最后一个元素)作为分界点,把整个数组分成左右两侧,左边的元素小于或者等于分界点元素,而右边的元素大于分界点元素,然后把分界点移到中间位置,对左右子数组分别进行递归,最后就能得到一个排序完成的数组。当子数组只有一个或者没有元素的时候就结束这个递归过程。
其中最重要的是将整个数组根据分界点元素划分成左右两侧的逻辑,目前有两种算法。
leetcode示例
912.排序数组
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 50000
-50000 <= nums[i] <= 50000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-an-array
C++代码实现
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
quicksort(nums,0,nums.size()-1);
return nums;
}
// 第一种快排
private:
void quicksort(vector<int>& nums,int left, int right){
if(left >= right) return;
int li = left + 1;//小于某值nums[left]的最右侧值下标
int hi = right;
while(li <= hi){
if(nums[li] > nums[left]){
swap(nums,li,hi);
hi--;
}else{
li++;
}
}
li--;
swap(nums,li,left);
quicksort(nums,left, li - 1);
quicksort(nums, li + 1, right);
}
//第二种快排
void quicksort(vector<int>& nums,int left, int right){
if(left >= right) return;
int li = left;//小于某值nums[left]的最右侧值下标
int hi = left+1;
while(hi <= right){
if(nums[hi] < nums[left]){
swap(nums,li+1,hi);
li++;
}
hi++;
}
swap(nums,li,left);
quicksort(nums,left, li - 1);
quicksort(nums, li + 1, right);
}
void swap(vector<int>& nums,int m,int n){
int temp = nums[m];
nums[m] = nums[n];
nums[n] = temp;
}
}
第二种,不用hi
来标记大于分界点元素的最左侧,而是只用一个li
来标记最右侧。在遍历整个数组的过程中,如果发现了一个小于等于分界点元素的元素,就和li+1
位置的元素交换,然后li
自增,这样可以保证li
的左侧一定都是小于等于分界点元素的,遍历到最后li
的位置就是新的分界点位置,和最开始的分界点元素位置互换。
时间复杂度在最佳情况是O(nlogn),但是如果分界点元素选择不当可能会恶化到O(n2),但是这种情况比较少见(比如数组完全逆序),如果随机选择分界点的话,时间复杂度能够稳定在O(nlogn)。另外如果元素中相同元素数量比较多的话,也会降低排序性能。
空间复杂度在O(logn)水平,属于堆栈调用,在最坏的情况下空间复杂度还是O(n),平均情况下复杂度是O(logn)。
快速排序是不稳定的,因为包含跳跃式交换元素位置。