上一次我說到所謂的「非遞歸」快速排序算法,不過是用棧來消除了遞歸,它的運行時間確定比遞歸算法長,咱們不妨來實際實現一下。代碼以下:算法
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 #define MAX_TOP 10000 /*一個很大的棧*/ 6 #define NUM 500L 7 8 /*有關棧的數據結構*/ 9 struct Region { 10 long left; 11 long right; 12 }; 13 14 struct Stack { 15 struct Region reg[MAX_TOP+1]; 16 long top; 17 }; 18 19 /*對棧進行操做的函數*/ 20 void init_stack(struct Stack *s); 21 void push_stack(struct Stack *s, struct Region r); 22 struct Region pop_stack(struct Stack *s); 23 int is_stack_empty(struct Stack *s); 24 25 /*與排序有關的函數*/ 26 27 long partition(double a[], long left, long right); /*劃分區間*/ 28 void nr_qsort(double a[], long left, long right); 29 30 31 int main(void) 32 { 33 double a[NUM]; /*待排序數據*/ 34 clock_t t_s, t_e; 35 long i; 36 37 srand(time(NULL)); 38 for (i = 0; i < NUM; ++i) 39 a[i] = rand() % 1000000; 40 41 /*統計運行時間*/ 42 t_s = clock(); 43 nr_qsort(a, 0, NUM-1); 44 t_e = clock(); 45 double t = (t_e - t_s) / (double) CLOCKS_PER_SEC; 46 printf("Non Recursive quick sort %ld items used time: %f s\n", NUM, t); 47 48 return 0; 49 } 50 51 52 /*implementation*/ 53 54 void init_stack(struct Stack *s) 55 { 56 s->top = -1; 57 } 58 59 void push_stack(struct Stack *s, struct Region r) 60 { 61 if (s->top == MAX_TOP) { 62 fprintf(stderr, "Stack overflow!\n"); 63 exit(0); 64 } 65 s->reg[++s->top] = r; 66 } 67 68 struct Region pop_stack(struct Stack *s) 69 { 70 if (s->top == -1) { 71 fprintf(stderr, "Stack underflow!\n"); 72 exit(0); 73 } 74 return (s->reg[s->top--]); 75 } 76 77 int is_stack_empty(struct Stack *s) 78 { 79 return (s->top == -1); 80 } 81 82 /*返回劃分的區間*/ 83 long partition(double a[], long left, long right) 84 { 85 double base = a[left]; /*以最左邊的元素做爲比較基準*/ 86 87 while (left < right) { 88 while (left < right && a[right] > base) 89 --right; 90 a[left] = a[right]; 91 while (left <right && a[left] < base) 92 ++left; 93 a[right] = a[left]; 94 } 95 a[left] = base; 96 return left; 97 } 98 99 void nr_qsort(double a[], long left, long right) 100 { 101 struct Stack s; 102 struct Region region, regionlow, regionhi; 103 long p; /*記錄劃分出的分界點*/ 104 105 init_stack(&s); 106 region.left = left; 107 region.right = right; 108 push_stack(&s, region); 109 110 while (!is_stack_empty(&s)) { 111 region = pop_stack(&s); 112 p = partition(a, region.left, region.right); 113 if (p-1 > region.left) { 114 regionlow.left = region.left; 115 regionlow.right = p - 1; 116 push_stack(&s, regionlow); 117 } 118 if (region.right > p + 1) { 119 regionhi.left = p + 1; 120 regionhi.right = region.right; 121 push_stack(&s, regionhi); 122 } 123 } 124 125 }
在代碼的第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
這也印證了上一次談到的系統默認限制帶來的問題。測試