排序算法-快速排序(快排、双路快排、三路快排)(1) C++实现

快速排序也是一种复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)的排序算法,但它的效率比同等复杂的排序算法还要高,所以在实践中会经常使用。

原理

快速排序的思想是将数组分为两个部分,一部分小于 v v v,另一部分大于 v v v,这个 v v v也称为基准数,一般选取数组第一个数作为基准数。它的实现效果是这样的,当分为两个数组后,每个单独的数组又可以使用该方法排序,直到只有最后一个元素,那这里又可以使用递归方法了。我们将数组分为两部分的过程称为partition。
在这里插入图片描述
partition的实现思路如下:假设第一个元素为基准点,并将其索引记为 l l l,此时已经有一部分数据进行了整理,得到了 < v <v <v,和 > v >v >v的部分,规定 a r r [ l + 1... j ] < v arr[l+1...j]<v arr[l+1...j]<v a r r [ j + 1... i − 1 ] > v arr[j+1...i-1]>v arr[j+1...i1]>v i i i表示遍历中当前元素索引。
在这里插入图片描述
如果 e > v e>v e>v就很简单了,直接将索引值向后移动一位即可
在这里插入图片描述
如果 e < v e<v e<v,稍微麻烦一点,将 > v >v >v部分的第一个数据与 e e e交换,同时 j + + , i + + j++,i++ j++,i++即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
遍历完成后,将 v v v a r r [ j ] arr[j] arr[j]交换, v v v也就放在了正确的位置。在这里插入图片描述
此时 a r r [ l . . . j − 1 ] < v arr[l...j-1]<v arr[l...j1]<v a r r [ j + 1... i ] > v arr[j+1...i]>v arr[j+1...i]>v, a r r [ j ] = v arr[j]=v arr[j]=v,接下来对两部分数组分别排序即可。
梳理以上过程,可得快排的实现步骤:
1.partition将数组正确分为两部分
2.对两部分递归partition,直至递归结束。

实现

#include <iostream>
#include <algorithm>
template <typename T>
int partition(T arr[], int l, int r){
	T v = arr[l];
	//arr[l+1,j]<v,arr[j+1,i)>v
	int j=l;
	for(int i=l+1; i<=r; i++){ //注意是<=r,而不是<r
		if(arr[i]<v){
			swap(arr[i],arr[j+1]);
			j++;
		}
	}
	swap(arr[j],arr[l]);
	return j;
}
template <typename T>
void __quickSort(T arr[], int l, int r){
	if(l>=r)
		return;
	int p = partition(arr, l ,r);
	__quickSort(arr, l, p-1);
	__quickSort(arr, p+1, r);
}
template <typename T)
void quickSort(T arr[], int n){
	__quickSort(arr, 0, n-1);
}

优化

首先,对于小规模数组排序可以采用插入排序,提高小规模数组排序运行速度。具体代码只需要将递归结束的条件改为插入排序。

template <typename T>
void __quickSort(T arr[], int l, int r){
	if(r-l<=15){
		insertionSort(arr,l,r);   //具体插入排序的代码可以参考之前的文章
		return;
	}
	int p = partition(arr, l ,r);
	__quickSort(arr, l, p-1);
	__quickSort(arr, p+1, r);
}

但这只是一个小小的修改,此时的快速排序有一个致命的弱点会使时间复杂度变为 O ( n 2 ) O(n^2) O(n2)。这个问题就出现在解决近乎有序数组上,如果一个有序数组,快速排序每次选择第一个元素作为基准值,在partition操作后基准值的位置几乎没有移动,那就相当于 n n n个数据被分为1 和 n − 1 n-1 n1个,那么到最后就需要递归n次,同时partition的操作时间复杂度也是 O ( n ) O(n) O(n),此时的复杂度就退化为 O ( n 2 ) O(n^2) O(n2)了。所以如果能够尽量选择中间的元素,那么复杂度就能够达到 O ( n l o g n ) O(nlogn) O(nlogn)
在这里插入图片描述
为了解决这个问题,我们可以随机选择一个数作为基准数,这样就可以极大的减小每次partition后数组的不均衡性。此时在最差的情况下快排的时间复杂度仍然为 O ( n 2 ) O(n^2) O(n2),但是这种概率是非常低的,它的时间复杂度期望为 O ( n l o g n ) O(nlogn) O(nlogn)

#include <iostream>
#include <algorithm>
#include <ctime>
template <typename T>
int partition(T arr[], int l, int r){
	swap(arr[l],arr[rand()%(r-l+1)+l);
	T v = arr[l];
	//arr[l+1,j]<v,arr[j+1,i)>v
	int j=l;
	for(int i=l+1; i<=r; i++){ //注意是<=r,而不是<r
		if(arr[i]<v){
			swap(arr[i],arr[j+1]);
			j++;
		}
	}
	swap(arr[j],arr[l]);
	return j;
}
template <typename T>
void __quickSort(T arr[], int l, int r){
	if(r-l<=15){
		insertionSort(arr,l,r); 
		return;
	}
	int p = partition(arr, l ,r);
	__quickSort(arr, l, p-1);
	__quickSort(arr, p+1, r);
}
template <typename T)
void quickSort(T arr[], int n){
	srand(time(NULL));
	__quickSort(arr, 0, n-1);
}

参考:https://coding.imooc.com/learn/list/71.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值