经典的快速排序是以第一的数为pivot来划分每次的标准的,优化过程可以使用三数取中,小段数字插排,重复pivot合并等方法来进行操作。这里先给出一个经典的快排代码,以任意非数字结束输入。
//快速排序以及进阶操作
// pivot: the key
// 不能排序负数
#include"stdio.h"
#define INT_MIN -2147483648
void qst(int*,int,int);
int get()
{
int judge;
int q;
if (judge = scanf("%d", &q))
return q;
else
return INT_MIN;
}
void swap(int* a, int* b)
{
int box;
box = *a;
*a = *b;
*b = box;
}
int main()
{
int s[100000]={0};
int i = 0,n;
while ((n = get())!= INT_MIN)
{
s[i] = n;
i++;
}
qst(s,0,i-1);
while(i--)
{
printf("%d ",s[i]);
}
puts("");
return 0;
}
//以#结束输入
void qst(int* que,int begin,int end)
{
if (begin>end)
return;
int pivot;
int start = begin,last=end;
pivot = que[begin];
while (begin != end)
{
while (que[end] >= pivot && begin != end)
end--;
if (begin!=end)
swap(&que[begin], &que[end]);
while (que[begin] <= pivot && begin != end)
begin++;
if (begin != end)
swap(&que[begin], &que[end]);
}
qst(que, start,begin-1);
qst(que, begin+1,last);
}
然后这里再给出一个优化后的快排代码,运算速度接近STL中的sort函数。
其中我使用的三数取中的方法比较笨,必须要标记pivot 的所在位置,不然在进入下一级排序时标志会乱,导致排序失败。加了三数取中后的排序方法后比较关键的一个变化就是pivot的位置决定了在进入下一级前需要和谁换位置(不知道可以这样理解不),这也是这个优化的关键操作。
后面还加入了一个去重的优化,这个该怎么实现呢?在看了一些实践的方式后我决定使用一个O(n)的算法使排序后的数组中的重复数字集中在pivot左右,这样子下一次只需要处理重复pivot的两端数组即可。例如:79 89 5 79 79 99 32 66 79 99.在一次处理中为:1.(三数取中找pivot)pivot=79,2.交换:79 66 5 79 79 32 99 89 79 99(begin停在了32的位置),3.去重:先交换79与32;79 66 5 79 99 32 79 89 79 99,然后去重:(围绕begin=6进行去重)32 66 5 99 79 79 79 79 89 99,同理,其他的过程类似,这就是我给出的一个优化快排的完整版代码,欢迎各位dl纠正。
#include"stdio.h"
void qst(int*, int, int);
void swap(int* a, int* b)
{
int box;
box = *a;
*a = *b;
*b = box;
}
int median(int a, int b, int c)//三数取中优化法
{
if (a <= b && a >= c)
return 1;
else if (b <= a && b >= c)
return 2;
else
return 3;
}
void InsertSort(int* q, int a, int b)
{
int now;
for (int i = a + 1; i <= b; i++)
{
if (q[i] < q[i - 1])
{
now = i;
while (q[now] < q[now - 1] && now>0)
{
swap(&q[now], &q[now - 1]);
now--;
}
}
}
}
void qst(int* que, int begin, int end)
{
if (begin > end)
return;
if (end - begin < 10)
{
InsertSort(que, begin, end);
return;
}
int pivot, j;
int start = begin, last = end;
pivot = median(que[begin], que[end], que[(begin + end) / 2]);
if (pivot == 1)
{
pivot = que[begin];
j = begin;
}
else if (pivot == 2)
{
pivot = que[end];
j = end;
}
else
{
pivot = que[(begin + end) / 2];
j = (begin + end) / 2;
}
while (begin != end)
{
while (que[end] >= pivot && begin != end)
end--;
while (que[begin] <= pivot && begin != end)
begin++;
if (begin != end)
{
swap(&que[begin], &que[end]);
}
}
//理解为什么需要换位置......
//delete repeated elements......
int s = start, e = last;
if (j <= begin)
{
int left = begin - 1, right = begin + 1;
que[j] = que[begin];
que[begin] = pivot;
for (; s <= left; s++)
{
if (que[s] == pivot)
{
swap(&que[s], &que[left]);
left--;
}
}
for (; e >= right; e--)
{
if (que[e] == pivot)
{
swap(&que[e], &que[right]);
right++;
}
}
qst(que, start, left);
qst(que, right, last);
}
else if (j > begin)
{
int left = begin, right = begin + 2;
que[j] = que[begin + 1];//交换位置的顺序不能错......
que[begin + 1] = pivot;
for (; s <= left; s++)
{
if (que[s] == pivot)
{
swap(&que[s], &que[left]);
left--;
}
}
for (; e >= right; e--)
{
if (que[e] == pivot)
{
swap(&que[e], &que[right]);
right++;
}
}
qst(que, start, left);
qst(que, right, last);
}
}
int main()
{
int s[100001] = { 0 };
int i = 0, n;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", &s[i]);
}
qst(s, 0, i - 1);
for (n = 0; n < i; n++)
{
if (n < i - 1)
printf("%d ", s[n]);
else
printf("%d", s[n]);
}
puts("");
return 0;
}