实现细节(注意事项):(针对特殊测试用例:顺序数组或者逆序数组)一定要随机化选择切分元素(pivot),否则在输入数组是有序数组或者是逆序数组的时候,快速排序会变得非常慢(等同于冒泡排序或者「选择排序」)
随机化枢纽
快排代码模板(递归版):
class Solution {
public:
void Qsort(vector<int>& nums, int L, int R){
if(L>=R) return;
//随机化取pivot
int x = rand() % (R - L + 1) + L;
swap(nums[L], nums[x]);
int pivot = nums[L];
int i=L, j=R;
while(i<j){
while(i<j && nums[j]>=pivot) j--;
while(i<j && nums[i]<=pivot) i++;
if(i<j) swap(nums[i],nums[j]);
}
swap(nums[L],nums[i]);
Qsort(nums, L, i-1);
Qsort(nums, i+1, R);
}
vector<int> sortArray(vector<int>& nums) {
int size=nums.size();
Qsort(nums, 0, size-1);
return nums;
}
};
快排思想升级版算法——快速选择(减治法)
模式识别:确定数据量的情况下寻找第K大的数
快速选择算法:快速排序算法中的轴值计算
class Solution {
public:
//所以只要某次划分的 q 为倒数第 k 个下标的时候,我们就已经找到了答案。 我们只关心这一点,至于 a[l⋯q−1]和 a[q+1⋯r]是否是有序的,我们不关心。
//⭐⭐
//因此我们可以改进快速排序算法来解决这个问题:在分解的过程当中,我们会对子数组进行划分
//如果划分得到的 q 正好就是我们需要的下标,就直接返回 a[q];
//否则,如果 q 比目标下标小,就递归右子区间,否则递归左子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。这就是 ⭐「快速选择」算法。
//引入随机化来加速这个过程,它的时间代价的期望是 O(n)
//这个时间复杂度只有在 随机数据 下才成立,而对于精心构造的数据则可能表现不佳。因此我们这里并没有真正地使用随机数,而是使用双指针的方法,这种方法能够较好地应对各种数据。
//⭐快速选择
//以下为单个划分过程:
//整个快排过程的体现:不停的左右递归实现
int partition(vector<int>& nums, int L, int R){
int index = rand()%(R-L+1)+L;
int pivot = nums[index];
swap(nums[L],nums[index]);
int i=L, j=R;
while(i<j){
while(i<j && nums[j]<=pivot) j--;
while(i<j && nums[i]>=pivot) i++;
if(i<j) swap(nums[i],nums[j]);
}
swap(nums[L],nums[i]);
return i;
}
int findKthLargest(vector<int>& nums, int k) {
int size = nums.size();
int L=0,R=size-1;
while(true){
int index = partition(nums, L, R);
if(index == k-1) return nums[index];
else if(index<k-1) L=index+1;
else R=index-1;
}
}
};
双指针
三路快排
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return quickSelect(nums, k);
}
private:
int quickSelect(vector<int>& nums, int k) {
// 随机选择基准数
int pivot = nums[rand() % nums.size()];
// 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
vector<int> big, equal, small;
for (int num : nums) {
if (num > pivot)
big.push_back(num);
else if (num < pivot)
small.push_back(num);
else
equal.push_back(num);
}
// 第 k 大元素在 big 中,递归划分
if (k <= big.size())
return quickSelect(big, k);
// 第 k 大元素在 small 中,递归划分
if (nums.size() - small.size() < k)
return quickSelect(small, k - nums.size() + small.size());
// 第 k 大元素在 equal 中,直接返回 pivot
return pivot;
}
};