快速排序算法的几种实现

来源:互联网 发布:网络电视怎么看电影 编辑:程序博客网 时间:2024/05/17 07:52

1.一种简单的快速排序:


序列范围[l,u]

选取第一个元素为枢纽值,然后围绕t划分数组:

m=a-1for i =[a,b]  if x[i]<t     swap(x[++m],x[i])

其中a=l+1,b=u。

循环终止后有:


此时交换x[l]和x[m]即可。

代码:

void qsort1(int *x, int l, int u){if (l >= u)return;int m = l;for (int i = l+1; i <= u; ++i){if (x[i] < x[l])swap(x[++m], x[i]);}swap(x[l], x[m]);qsort(x, l, m - 1);qsort(x, m + 1, u);}

可以通过qsort1(0,n-1)来排序数组x[n]

qsort1函数能够快速完成随机序列的排序,但是对于序列由n个相同的元素组成时,性能就非常差,从代码中我们可以看出,当序列元素相同时,qsort1的时间复杂度为O(n²)。

使用双向划分可以解决这个问题。

2.稍微改进的快速排序

对于上面的快速排序,我们可以稍微改进:
代码:
void qsort2(int *x, int l, int u){if (l >= u)return;int m = u+1;for (int i = u; i >=l; --i){if (x[i] >= x[l])swap(x[--m], x[i]);}qsort2(x, l, m - 1);qsort2(x, m + 1, u);}


通过将从左到右的循环修改为从右向左,从而省去一次swap:

最后一次循环结束前:

3.更好的几种快速排序算法

使用双向划分的快速排序:


i指示小于等于t的增长边界,j指向大于等于t的增长边界。

代码:

void qsort3(int *x, int l, int u){if (l >= u)return;int t = x[l];int i = l, j = u + 1;while (1){while (++i <= u && x[i] < t);while (x[j] > t);if (i > j)break;swap(x[i], x[j]);}swap(x[l], x[j]);qsort3(x, l, j - 1);qsort3(x, j + 1, u);}

当while(1)循环结束时,序列变成:


所以最后进行swap(x[l],x[j])。

这里特别要注意i和j,swap的是x[j]不是x[i]!!!

我们注意到:

while (++i <= u && x[i] < t);
while (x[j] > t);


为什么第二个while不写成while (--j >= l && x[j] > t)?因为没有必要,序列的第一个元素为t,当j==l时有x[j]==t,该循环总是能终止不会发生越界情况(相当于第一个元素为哨兵),而第一个while则不同。

当遇到和t相同的元素时,停止扫描并交换x[i]和x[j],这样做虽然交换的次数增加了,但却将所有元素相同的最坏情况变成了差不多需要nlogn次比较的最好情况。


但是,如果序列已经排好序了,上面的代码性能依然会下降到O(n²),原因是我们每次都是选择第一个元素作为划分元素。我们可以通过随机选择划分元素来解决:

int c=randint(l,u);swap(x[l],x[c]);
其中函数randint(l,u)产生[l,u]之间的随机数。