C++ 三路快排 模板
前言:今天被大作业的快速排序折磨的焦头烂额,原 C++
sort
选手发现简洁的快排竟然如此难写(边界要注意的点好多 qwq)。
我原先的快排长这样:题解 P1177 【【模板】快速排序】 - 沧海之耀 的博客 - 洛谷博客 (luogu.com.cn)
三路快排:快速排序 - OI Wiki (oi-wiki.org)
先在 arr[l,r]
中随机一个 val
快排当前层的目标是将数值分为 < val | = val | > val
三类,也按这个顺序分开,然后分别递归 < val
和 > val
两类。
算法思路:
-
在工作过程中,维护
arr[l,r]
的这样一个性质。arr[l,j) < val | arr[j,i) = val | arr[i,k] 未分类 | arr(k,r] > val
只要从左到右扫描
i
,直至arr[i,k]
为空即可。 -
变量定义
-
i
: 要分类的对象 (从左到右扫描) ,[l,i)
是已经分类好的< val | = val
-
j
:arr[l,j) < val, arr[j,i) = val
,j
表示 第一个 值为val
的下标,若暂时没有=val
的值,则j=i
。 -
k
: 维护一个右侧> val
的垃圾堆。(k,r]
是> val
的部分
-
-
工作流程:
-
从左到右扫描
-
arr[i] > val
的放到右侧(与arr[k]
交换),这里i
不能右移,因为 换过来的arr[k]
还要判断。 -
arr[i] < val
的与arr[j]
交换,i
右移,j
右移,维护arr[l,j) < val, arr[j,i) = val
。 -
arr[i] = val
,不动,i
右移即可。
-
-
具体实现:
// 三路快排
void qsort(int arr[],int l,int r)
{
if(l>=r) return;
int i=l,j=l,k=r;
int val=arr[l+rand()%(r-l+1)];
while(i<=k) {
if(arr[i]<val)
swap(arr[i++],arr[j++]); // 这里可,前面是安全的。
else if(val<arr[i])
swap(arr[i],arr[k--]); // 这里 i 不能 ++, 还要判断一下后面扔过来的东西。
else i++;
}
qsort(arr,l,j-1); qsort(arr,k+1,r);
}
-
时间复杂度分析:
-
每次循环 \(i\) 增加或 \(k\) 减少,于是每层 \(O(len)\)
-
总共 平均 \(O(n\log n)\)
-
这样实现三路快排, 比较好的解决了相同值多,或者选到最小或最大的 val 导致问题规模不缩小的问题。排序一次问题规模必缩小 。