El pueblo unido jamas serà vencido!

[整活]QYT归并排序(从二路归并到三路归并)

前言

闲大发了属于是。

不过过年就是要整点让人开心的(?)

归并排序

归并排序以分治的思维处理一个无序的序列。

  1. 将序列均分为两份,若不能再分则直接进入步骤 3
  2. 分别排序均分后的两序列
  3. 现在得到的两个序列都是有序的,每次取出两个序列中较大/较小的那个放在新有序序列最前的位置即可合并两个序列

通过主定理分析可得,其平均复杂度及最劣复杂度均为 \(\mathcal{O} (n \log n)\)

void MergeSort(int *a,int l,int r) {
	if(l == r) return ;
	int mid = (l + r) >> 1;static int t[N];
	MergeSort(a,l,mid),MergeSort(a,mid + 1,r);
	int p1 = l,p2 = mid + 1,p = l;
	while(p1 <= mid && p2 <= r) {
		if(a[p1] <= a[p2]) t[p++] = a[p1++];
		else t[p++] = a[p2++];
	}
	while(p1 <= mid) t[p++] = a[p1++];
	while(p2 <= r) t[p++] = a[p2++];
	rep(i,l,r) a[i] = t[i];
}

三路归并

在上面的过程中,我们将序列分成两份。

但是我们知道,如果能将序列分成自然对数的底数 \(e\) 份所得的效率是这类分治然后合并的排序能得到的最好效率,但是 \(e \approx 2.718281828459\),相对于 \(2\),其与 \(3\) 更接近。那么尝试将序列均分为三段然后合并三个有序序列。

复杂度分析

考虑合并的过程,每个元素只被加入临时数组一次,为 \(\mathcal{O} (n)\) 的。

利用主定理 :

\[\large T(n) = 3T(\frac{n}{3}) + \mathcal{O}(n) = \mathcal{O}(n \log_3 n) \]

然后你就可以发现这个玩意大常数。

但是这不妨碍它和二路归并差不多快,于是我们将其命名为 QYT 归并排序。

这个排序的优点是码量比归并大,可以锻炼你输入的速度(?)

以下是乱写的代码。

void QytMergeSort(int *a,int l,int r) {
	if(l >= r) return ;
	static int t[N];
	int mid1 = l + (r - l) / 3,mid2 = r - (r - l) / 3;
	QytMergeSort(a,l,mid1);
	QytMergeSort(a,mid1 + 1,mid2);
	QytMergeSort(a,mid2 + 1,r);
	int p = l,p1 = l,p2 = mid1 + 1,p3 = mid2 + 1;
	while(p1 <= mid1 && p2 <= mid2 && p3 <= r) {
		if(a[p1] <= a[p2] && a[p1] <= a[p3])
			t[p++] = a[p1++];
		else if(a[p2] <= a[p1] && a[p2] <= a[p3])
			t[p++] = a[p2++];
		else
			t[p++] = a[p3++];
	}
	
	while(p1 <= mid1 && p2 <= mid2) {
		if(a[p1] < a[p2]) t[p++] = a[p1++];
		else t[p++] = a[p2++];
	}
	while(p1 <= mid1 && p3 <= r) {
		if(a[p1] < a[p3]) t[p++] = a[p1++];
		else t[p++] = a[p3++];
	}
	while(p2 <= mid2 && p3 <= r) {
		if(a[p2] < a[p3]) t[p++] = a[p2++];
		else t[p++] = a[p3++];
	}
	
	while(p1 <= mid1) t[p++] = a[p1++];
	while(p2 <= mid2) t[p++] = a[p2++];
	while(p3 <= r) t[p++] = a[p3++];
	rep(i,l,r) a[i] = t[i];
}
posted @ 2022-02-01 21:37  AstatineAi  阅读(499)  评论(0编辑  收藏  举报