快速排序进阶之三路快排——学习笔记

最近才知道快速排序还分二路快排和三路快排,二路快排就是我们平时学习的普通快排,而三路快排虽然听起来好像要高大上一点,其实想明白了也就是一回事。

二路快排

先说说二路快排,也就是普通的快速排序。

排序的原理就是在待排序的数组中随机选择一个数key,把小于key的数统统放着key的左边,那么大于等于key的数都丢在右边了,得到一个新的数列:

[......] <  key  <= [......]     

这样一来key实际上已经排到了正确的位置上了,我们只需要对左右两边剩下的数进行相同的操作,最后就能得到一个排序好的数列。

看代码:

//交换x和y的值
void swap(int &x, int &y) {
	int temp=x;
	x = y;
	y = temp;
}

//a为数列,起点start,终点end(end为排序数组最后一位数的下标)
void FastSort(int *a, int start, int end) {
	if (start >= end) {
		return;
	}

	//Patition操作:将key放到合适的位置,使得其左边的数都不大于它,右边的数都不小于它
	int key = a[start];
	int j = start;
	for (int i = start + 1; i <= end; i++) {
		if (a[i] < key) {         //当a[i]<key时,交换a[++j]和a[i]
			swap(a[++j],a[i]);
		}
	}
	swap(a[start], a[j]);

	//对key两边的数继续进行排序
	FastSort(a,start, j - 1);
	FastSort(a,j + 1, end);
}

快排函数中间那段,就是著名的Partition函数,非常优美的操作~

三路快排

三路快排是为了应对数列中有大量的重复数据,为了避免对重复数据进行反复排序,将每次排列的数列分成三部分:

[......] < [key,key,...,key] < [......]

原理就是小的丢左边,大的数丢右边,剩下在中间的数就是与key相等的数了。这样处理后,再对key两边的数列进行排列,可以大大加快排列速度。来看看和二路快排代码有什么不同:

void FastSort2(int *a, int start, int end) {
	if (start >= end) {
		return;
	}

	//Partition操作:小于key的丢左边,大于key的丢右边
	int key = a[start];
	int i = start;
	int j = start;
	int k = end;
	while (i <= k) {
		if (a[i] < key) {
			swap(a[i++], a[j++]);
		}
		else if (a[i] > key) {
			swap(a[i], a[k--]);
		}
		else {
			i++;
		}
	}

	FastSort2(a, start, j);
	FastSort2(a, k + 1, end);
}

这次的Partition函数依旧很美,相比前面的二路快排,三路快排在扫描的过程中将数据按小于、大于、等于分别操作,最后将数据分为三路,相较于二路快排,效率进一步提高~

随机快排

快排的平均时间复杂度O[nlog(n)],最坏情况可能会达到O[n^2]。由于快排对应于乱序的数列排序速度较快,而越接近排好序的数列则排序速度越慢。为了解决这个问题,我们有时需要将数组打乱提高数列的混乱度,有助于提高排序速度。

这个实现方法很多,我也不做过多的介绍了~有兴趣可以自己实现一下。

猜你喜欢

转载自blog.csdn.net/jjwwwww/article/details/81057881