1.一种简单的快速排序:
序列范围[l,u]
选取第一个元素为枢纽值,然后围绕t划分数组:
m=a-1
for 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]之间的随机数。