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) = valj 表示 第一个 值为 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 导致问题规模不缩小的问题。排序一次问题规模必缩小 。

posted @ 2023-03-09 15:02  cjlworld  阅读(68)  评论(0编辑  收藏  举报