算法思想
在数组中选取一个基准元素,之后调整数组的顺序,基准元素的前部全部小于等于该元素,后部全部大于该元素。然后,递归的对前部和后部分别使用以上算法。所以该算法的重点在选取基准元素和调整顺序两个方面。
如果选取的基准元素在调整后的数组中正好处在中间,递归的层数最少,算法的效率将达到最高。因此基准元素的选取对该算法的时间复杂度影响很大。选取基准元素最简单的方法就是将数组的第一个元素当作基准元素。
这种方法虽然简单快速,但是如果需要排序的数组是一个事先排好序的
n
n
n个元素的数组,那么算法将会递归
n
n
n层,时间复杂度为
n
2
n^2
n2。为了弥补该缺陷,可以分别从数组的前部、中部和后部分别选取一个元素,将三个元素的中位数作为基准元素。使用该方法后,面对事先排号的数组可将时间复杂度降低到
n
log
2
n
n\log_2n
nlog2n。
选取基准元素还有一种靠运气的方法,那就是随机选取该数组中的某个元素作为基准元素。具体实现方法就是用随机数除余数组长度得到随机基准元素的下标,根据下标找到基准元素。
调整顺序的大概思想是:用两个指针从数组两侧向中间扫描,后部指针找到一个小于等于基准元素的元素就停止,前部指针找到一个大于基准元素的元素就停止,然后将两个指针指向的元素进行交换,然后继续扫描直到两个指针相遇,最后把基准元素放到中间。
时间复杂度
根据递推的关系可得出推算公式:
T
(
n
)
=
{
1
+
n
+
2
T
(
n
2
)
n
≥
2
0
n
=
1
T(n)=\left\{ \begin{array}{lrl} 1+n+2T(\frac{n}{2})&&n\geq2\\ 0&&n=1 \end{array} \right.
T(n)={1+n+2T(2n)0n≥2n=1
1
1
1是选取基准元素的时间,
n
n
n是整理数组的时间,
2
T
(
n
2
)
2T(\frac{n}{2})
2T(2n)是前部和后部两个数组所需的时间。最终计算得出:
T
(
n
)
∈
Θ
(
n
log
2
n
)
T(n)\in\Theta(n\log_{2}n)
T(n)∈Θ(nlog2n)。
将数组第一个元素作为基准元素测试结果
将数组前部、中部和后部三个元素的中位数作为基准元素测试结果
随机选取该数组中的某个元素作为基准元素测试结果
测试代码
将数组第一个元素作为基准元素
#include <iostream>
using namespace std;
typedef int element;
void quick_base(element *s,element *e){///传入指向数组前部元素和后部元素的指针
if(e-s>0){
element m = *s;///获取基准元素
element *i = s;///小元素指针
element *j = e;///大元素指针
while(j>i){
while(*j>m&&j>i){///j指向的元素大于基准元素
j--;///指针前移
}
*i = *j;///此时j指向的元素小于等于基准元素
while(*i<=m&&j>i){///i指向的元素小于等于基准元素
i++;///指针后移
}
*j = *i;///此时i指向的元素大于基准元素
}///指针重叠时退出
*i = m;
quick_base(s,i-1);///对基准元素前的进行快排
quick_base(i+1,e);///对基准元素后的进行快排
}
}
void quick(element *s,size_t n){///传入数组和数组长度
quick_base(s,s+n-1);
}
int main(){
element test[] = {
41,467,334,500,169,724,478,358,962,464,
705,145,281,827,961,491,995,942,827,436,
391,604,902,153,292,382,421,716,718,895,
447,726,771,538,869,912,667,299,35,894,
703,811,322,333,673,664,141,711,253,868,
547,644,662,757,37,859,723,741,529,778,
316,35,190,842,288,106,40,942,264,648,
446,805,890,729,370,350,6,101,393,548,
629,623,84,954,756,840,966,376,931,308,
944,439,626,323,537,538,118,82,929,541,
};
quick(test,sizeof(test)/sizeof(element));
for(size_t i=0;i<sizeof(test)/sizeof(element);i++){
cout << test[i] << endl;
}
return 0;
}
将数组前部、中部和后部三个元素的中位数作为基准元素
#include <iostream>
using namespace std;
typedef int element;
void quick_base(element *s,element *e){///传入指向数组前部元素和后部元素的指针
if(e-s>0){
element m;///获取基准元素
element *k = s+(size_t)(e-s+1)/2;
if(*k<*s){
if(*k<*e){
if(*s<*e){///中位数在前面
m = *s;
}else{///中位数在后面
m = *e;
*e = *s;
}
}else{///中位数在中间
m = *k;
*k = *s;
}
}else{
if(*k<*e){///中位数在中间
m = *k;
*k = *s;
}else{
if(*s<*e){///中位数在后面
m = *e;
*e = *s;
}else{///中位数在前面
m = *s;
}
}
}
element *i = s;///小元素指针
element *j = e;///大元素指针
while(j>i){
while(*j>m&&j>i){///j指向的元素大于基准元素
j--;///指针前移
}
*i = *j;///此时j指向的元素小于等于基准元素
while(*i<=m&&j>i){///i指向的元素小于等于基准元素
i++;///指针后移
}
*j = *i;///此时i指向的元素大于基准元素
}///指针重叠时退出
*i = m;
quick_base(s,i-1);///对基准元素前的进行快排
quick_base(i+1,e);///对基准元素后的进行快排
}
}
void quick(element *s,size_t n){///传入数组和数组长度
quick_base(s,s+n-1);
}
int main(){
element test[] = {
41,467,334,500,169,724,478,358,962,464,
705,145,281,827,961,491,995,942,827,436,
391,604,902,153,292,382,421,716,718,895,
447,726,771,538,869,912,667,299,35,894,
703,811,322,333,673,664,141,711,253,868,
547,644,662,757,37,859,723,741,529,778,
316,35,190,842,288,106,40,942,264,648,
446,805,890,729,370,350,6,101,393,548,
629,623,84,954,756,840,966,376,931,308,
944,439,626,323,537,538,118,82,929,541,
};
quick(test,sizeof(test)/sizeof(element));
for(size_t i=0;i<sizeof(test)/sizeof(element);i++){
cout << test[i] << endl;
}
return 0;
}
随机选取该数组中的某个元素作为基准元素
#include <iostream>
#include <random>
using namespace std;
typedef int element;
void quick_base(element *s,element *e){///传入指向数组前部元素和后部元素的指针
if(e-s>0){
element m;///获取基准元素
element *k = s+(rand()%(size_t)(e-s+1));
m = *k;
*k = *s;
element *i = s;///小元素指针
element *j = e;///大元素指针
while(j>i){
while(*j>m&&j>i){///j指向的元素大于基准元素
j--;///指针前移
}
*i = *j;///此时j指向的元素小于等于基准元素
while(*i<=m&&j>i){///i指向的元素小于等于基准元素
i++;///指针后移
}
*j = *i;///此时i指向的元素大于基准元素
}///指针重叠时退出
*i = m;
quick_base(s,i-1);///对基准元素前的进行快排
quick_base(i+1,e);///对基准元素后的进行快排
}
}
void quick(element *s,size_t n){///传入数组和数组长度
quick_base(s,s+n-1);
}
int main(){
element test[] = {
41,467,334,500,169,724,478,358,962,464,
705,145,281,827,961,491,995,942,827,436,
391,604,902,153,292,382,421,716,718,895,
447,726,771,538,869,912,667,299,35,894,
703,811,322,333,673,664,141,711,253,868,
547,644,662,757,37,859,723,741,529,778,
316,35,190,842,288,106,40,942,264,648,
446,805,890,729,370,350,6,101,393,548,
629,623,84,954,756,840,966,376,931,308,
944,439,626,323,537,538,118,82,929,541,
};
quick(test,sizeof(test)/sizeof(element));
for(size_t i=0;i<sizeof(test)/sizeof(element);i++){
cout << test[i] << endl;
}
return 0;
}
#include <iostream>
using namespace std;
typedef int element;
void quick_base(element *s, element *e){
if(e - s > 0){
element n = *s, *i = s - 1, *j = e + 1;
while(true){
while(*++i < n);
while(*--j > n);
if(i < j) swap(*i, *j);
else break;
}
quick_base(s, j);
quick_base(j+1, e);
}
}
void quick(element *s, size_t n){
quick_base(s, s + n - 1);
}
int main(){
element test[] = {
41,467,334,500,169,724,478,358,962,464,
705,145,281,827,961,491,995,942,827,436,
391,604,902,153,292,382,421,716,718,895,
447,726,771,538,869,912,667,299,35,894,
703,811,322,333,673,664,141,711,253,868,
547,644,662,757,37,859,723,741,529,778,
316,35,190,842,288,106,40,942,264,648,
446,805,890,729,370,350,6,101,393,548,
629,623,84,954,756,840,966,376,931,308,
944,439,626,323,537,538,118,82,929,541,
};
quick(test,sizeof(test)/sizeof(element));
for(size_t i=0;i<sizeof(test)/sizeof(element);i++){
cout << test[i] << endl;
}
return 0;
}