您的位置:首页 > 其它

算法导论例程——快速排序

2016-02-05 20:37 351 查看
快速排序是一种最坏情况复杂度为o(n^2),平均时间复杂度为o(nlgn)的排序算法,且nlgn隐含的常数因子非常小,最坏情况发生的概率也不高,进行的是原址排序,所以是采用度最高的一种排序算法,在c++的stdlib库中特别封装了qsort函数方便用户使用。

快速排序是冒泡排序的一种升级版本,主要思想就是选择数组中的一个值作为关键字(key),将数组中所有比key小的放在key前,比key大的放在key后,之后再对a[0,...key-1]和a[key+1,...end]递归地使用qsort过程,直到数组规模为1为止,这样就保证整个数组都是有序的了。

在实际过程中,一般选取数组最后一位作为key值,最初时将i的值选在数组外,之后遍历数组a[start,...,key-1],以i作为分界点,比key小的放在i左侧,大的放在i右侧,最后把a[key]与a[i]交换。

#include <stdio.h>
void qsort(int a[], int start, int end);
int divide(int a[], int start, int end);

int main()
{
int a[1000] = { 0 }, i = 0, n = 0, length = 0;
printf("请输入要排序的数组规模:");
scanf("%d", &n);
length = n;
while (n--)
scanf("%d", &a[i++]);

qsort(a, 0, length - 1);
i = 0;
while (length--)
printf("%d,", a[i++]);

return 0;
}

void qsort(int a[], int start, int end)
{
int key;
if (start < end)
{
key = divide(a, start, end);
qsort(a, start, key - 1);
qsort(a, key + 1, end);
}
}

int divide(int a[], int start, int end)
{
int key = 0, i = 0, j = 0,temp = 0;
key = a[end];
i = start - 1;
for (j = start; j < end; j++)
{
if (a[j] <= key)
{
i++;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
i++;
temp = a[i];
a[i] = a[end];
a[end] = temp;
return i;
}
要注意的是在交换值得时候不要写成下面这个样子,当i == j时会出现错误(变动的是同一个位置的数,最后就变成0了)





以上这种选key的方式也有一定局限性,当输入的恰好是升序时,每次分成的两个部分都是0和n-1,这样就会是运行时间达到o(n^2),改进的办法是用随机数法找到一个key值,这样就能保证每次选取都处于一种平均情况。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void qsort(int a[], int start, int end);
int divide(int a[], int start, int end);

int main()
{
int a[1000] = { 0 }, i = 0, n = 0, length = 0;
printf("请输入要排序的数组规模:");
scanf("%d", &n);
length = n;
while (n--)
scanf("%d", &a[i++]);

qsort(a, 0, length - 1);
i = 0;
while (length--)
printf("%d,", a[i++]);

return 0;
}

void qsort(int a[], int start, int end)
{
int key;
if (start < end)
{
key = divide(a, start, end);
qsort(a, start, key - 1);
qsort(a, key + 1, end);
}
}

int divide(int a[], int start, int end)
{
int key = 0, i = 0, j = 0, temp = 0, ran = 0;
srand((unsigned)time(NULL));
ran = start + rand() % (end - start);                                                 //注意范围
key = a[ran];
temp = a[end];
a[end] = a[ran];
a[ran] = temp;
i = start - 1;
for (j = start; j < end; j++)
{
if (a[j] <= key)
{
i++;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
i++;
temp = a[i];
a[i] = a[end];
a[end] = temp;
return i;
}
对于随机数法,还有一种更加细致地找到key值得方法——三数取中法,就是随机生成满足条件的三个数,之后取他们的中位数作为key值,这样选取对于样本来说更为“均衡”。
而对于原来的函数,我们可以很清楚地看出这是一个尾递归,由于递归对系统产生的额外开销(参数入栈 返回地址入栈 保存现场 进入子函数 返回地址出栈 恢复现场 ),我们应尽量减少递归的部分,因此做以下改写。
void tail_qsort(int a[], int start, int end)
{
int q;
while (start < end)
{
q = divide(a, start, end);
tail_qsort(a, start, q - 1);
p = q + 1;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: