您的位置:首页 > 其它

一次快速排序错误引发的思考(2)

2015-09-13 21:30 453 查看
  上一次我说到所谓的“非递归”快速排序算法,不过是用栈来消除了递归,它的运行时间肯定比递归算法长,我们不妨来实际实现一下。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_TOP 10000 /*一个很大的栈*/
#define NUM 500L

/*有关栈的数据结构*/
struct Region {
long left;
long right;
};

struct Stack {
struct Region reg[MAX_TOP+1];
long top;
};

/*对栈进行操作的函数*/
void init_stack(struct Stack *s);
void push_stack(struct Stack *s, struct Region r);
struct Region pop_stack(struct Stack *s);
int is_stack_empty(struct Stack *s);

/*与排序有关的函数*/

long partition(double a[], long left, long right);    /*划分区间*/
void nr_qsort(double a[], long left, long right);

int main(void)
{
double a[NUM];    /*待排序数据*/
clock_t t_s, t_e;
long i;

srand(time(NULL));
for (i = 0; i < NUM; ++i)
a[i] = rand() % 1000000;

/*统计运行时间*/
t_s = clock();
nr_qsort(a, 0, NUM-1);
t_e = clock();
double t = (t_e - t_s) / (double) CLOCKS_PER_SEC;
printf("Non Recursive quick sort %ld items used time: %f s\n", NUM, t);

return 0;
}

/*implementation*/

void init_stack(struct Stack *s)
{
s->top = -1;
}

void push_stack(struct Stack *s, struct Region r)
{
if (s->top == MAX_TOP) {
fprintf(stderr, "Stack overflow!\n");
exit(0);
}
s->reg[++s->top] = r;
}

struct Region pop_stack(struct Stack *s)
{
if (s->top == -1) {
fprintf(stderr, "Stack underflow!\n");
exit(0);
}
return (s->reg[s->top--]);
}

int is_stack_empty(struct Stack *s)
{
return (s->top == -1);
}

/*返回划分的区间*/
long partition(double a[], long left, long right)
{
double base = a[left];    /*以最左边的元素作为比较基准*/

while (left < right) {
while (left < right && a[right] > base)
--right;
a[left] = a[right];
while (left <right && a[left] < base)
++left;
a[right] = a[left];
}
a[left] = base;
return    left;
}

void nr_qsort(double a[], long left, long right)
{
struct Stack s;
struct Region region, regionlow, regionhi;
long p; /*记录划分出的分界点*/

init_stack(&s);
region.left = left;
region.right = right;
push_stack(&s, region);

while (!is_stack_empty(&s)) {
region = pop_stack(&s);
p = partition(a, region.left, region.right);
if (p-1 > region.left) {
regionlow.left = region.left;
regionlow.right = p - 1;
push_stack(&s, regionlow);
}
if (region.right > p + 1) {
regionhi.left = p + 1;
regionhi.right = region.right;
push_stack(&s, regionhi);
}
}

}


  在代码的第110行至第122行的while循环中,做的正是用栈消除递归的工作。想想递归的算法中,把划分好的左右区间界限(即left,right)保存到了系统管理的栈中,这里手动把每次划分出来的区间分界保存至栈中,当第113和118行的两个条件不满足时,所在区间的元素都是有序的状态,此时不进行压栈操作而向前返回(即递归的回调)。关于用栈消除递归的算法可以参考关于数据结构的书籍,比如陈锐的《零基础学数据结构》有关栈的那一章就有介绍。实际运行两个程序的结果如下:

$ ./nr_qsort  #非递归算法的快排
Non Recursive quick sort 500 items used time: 0.000261 s
$ ./qsort #递归算法的快排
Quick sort 500 items used time:0.000104 s


  之所以只用了500个数据,是因为超过1000个数据后,非递归快排的速度就慢的令人难以忍受。下面是另外两次关于递归算法快排的测试:

$ time ./qsort
Quick sort 1000000 items used time:0.289171 s

real    0m0.372s
user    0m0.332s
sys     0m0.012s

#下面更改NUM即数据的个数为10000000

$ ./qsort
Segmentation fault #超出栈的大小

$ ulimit -s unlimited #更改栈的大小为不受限
$ time ./qsort
Quick sort 10000000 items used time:3.259025 s #成功进行了排序

real    0m4.044s
user    0m3.740s
sys     0m0.172s


  这也印证了上一次谈到的系统默认限制带来的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: