一次快速排序错误引发的思考(2)
2015-09-13 21:30
453 查看
上一次我说到所谓的“非递归”快速排序算法,不过是用栈来消除了递归,它的运行时间肯定比递归算法长,我们不妨来实际实现一下。代码如下:
在代码的第110行至第122行的while循环中,做的正是用栈消除递归的工作。想想递归的算法中,把划分好的左右区间界限(即left,right)保存到了系统管理的栈中,这里手动把每次划分出来的区间分界保存至栈中,当第113和118行的两个条件不满足时,所在区间的元素都是有序的状态,此时不进行压栈操作而向前返回(即递归的回调)。关于用栈消除递归的算法可以参考关于数据结构的书籍,比如陈锐的《零基础学数据结构》有关栈的那一章就有介绍。实际运行两个程序的结果如下:
之所以只用了500个数据,是因为超过1000个数据后,非递归快排的速度就慢的令人难以忍受。下面是另外两次关于递归算法快排的测试:
这也印证了上一次谈到的系统默认限制带来的问题。
#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
这也印证了上一次谈到的系统默认限制带来的问题。
相关文章推荐
- ReentrantLock与synchronized的区别
- 【LeetCode从零单刷】Single Number III
- synchronized和ReentrantLock区别
- 短线交易秘诀 摘要
- LCA小结
- 1.ef 映射关系
- 超级课程表原理解析(如何获取网页内容)
- 快速生成xls模板
- 如何解决GDI+渲染图片慢的问题?
- 黑马程序员----反射
- SVM—当样本数小于维度的时候
- Spring MVC + AngularJS Integration
- 获取当前控制器的方法
- hdu5437 Alisha’s Party
- oracle for update
- [LeetCode-2] Add Two Numbers(链表数据之和)
- C语言实现单链表-01版
- Effective C++ —— 实现(五)
- Shell 脚本学习笔记-常用命令
- 多进程、多线程、同步、通信