一次快速排序錯誤引起的思考(1)

  快速排序是目前基於關鍵字的內部排序算法中平均性能最好的,它採用了分治策略,這既是快速排序的優勢也是它的缺點。從快速排序的算法描述上咱們能夠發現它具備遞歸的結構:算法

    (1)肯定一個分界,將待排序的數組分爲左、右兩個部分;數組

    (2)使全部小(大)於臨界值的數據移到左部分,大(小)於臨界值的數據移到右部分;函數

    (3)這時左、右兩個部分紅爲了兩個獨立的數組,分別對它們執行(1)(2)(3)的操做,直到全部數據都是有序的狀態爲止。性能

  照這樣的描述咱們不難寫出快排的代碼,我平時遇到排序的問題,只要數據量上了100,想都不想就用快排來解決,可是當我用下面這個程序測試時卻出現了問題:測試

 1 #include <stdio.h>
 2 #include <time.h>
 3 #include <stdlib.h>
 4 
 5 #define NUM 10000000    /*待排序的數據量*/
 6 
 7 void quick_sort(double a[], long left, long right);
 8 
 9 int main(void)
10 {
11     clock_t t_s, t_e; 
12     long i;
13     double a[NUM];
14     
15     srand(time(NULL));
16     for (i = 0; i < NUM; ++i) {
17         a[i] = rand();
18     }
19     
20     t_s = clock(); 
21     quick_sort(a, 0, NUM-1);
22     t_e = clock(); 
23     double t = (t_e - t_s) / (double)CLOCKS_PER_SEC;  /*計算排序用時*/
24     
25     printf("Quick sort %d items used time:%f s\n", NUM, t); 
26 
27     return 0;
28 }
29 
30 void quick_sort(double a[], long left, long right)
31 {
32     long i = left;
33     long j = right;
34     double mid = a[(i + j) / 2]; /*以中間元素做爲比較的基準*/
35 
36     while (i <= j) {
37         while (a[i] < mid)
38             ++i;
39         while (mid < a[j])
40             --j;
41         if (i <= j) {
42             double t = a[i];
43             a[i] = a[j];
44             a[j] =t;
45             ++i;
46             --j;
47         }
48     }
49 
50     if (i < right) quick_sort(a, i, right);
51     if (left < j) quick_sort(a, left, j);
52 }

  我在Linux上運行這個程序出現了"Segmentation fault "錯誤,而當NUM==1000000時卻沒有這個錯誤。查閱相關資料得知這是因爲程序遞歸次數太多,大量的壓棧使程序佔用的棧空間超過了操做系統所規定的大小,從而出現的內存錯誤。ui

  我用ulimit -s指令的獲得的結果是8192,也就是說個人系統默認給每一個程序分配的大概是8M的棧空間。用指令ulimit -s unlimited使棧空間變成實際內存大小後,上面的程序就能夠順利運行而不出錯誤了(由於Linux上不像Windows能夠把棧的大小寫入可執行文件中,因此只能用ulimit -s更改的方法了)。難道由於棧的限制,快速排序可以處理的數據量就有上限了嗎?那還不如用選擇排序——雖然慢,但至少不會出錯,因而我找到了這篇文章:快速排序的非遞歸實現。其實說是「非遞歸」,只不過是用本身管理的棧來消除遞歸,算法本質上沒有區別,並且從這篇文章做者的測試來看,用棧的方法比用遞歸的方法反而更慢(做者將其解釋爲:「用棧的效率比遞歸高,可是在這個程序中局部變量也就是要每次壓棧的數據不多,棧的優點體現不出來,反而更慢……」,我認爲這種觀點是不對的,因爲遞歸能夠理解爲有了一個「系統幫你自動管理的棧」,它的效率確定是要比你本身管理的棧要高的,何況你在進行彈棧和壓棧操做時又調用了新函數,算上調用的開支,用棧的方法確定比遞歸慢),不過棧在這裏的優點是能夠不用考慮操做系統的問題,並且可以處理的數據量只和內存大小有關,沒必要受到操做系統對棧空間大小的限制(即便用棧,快排也比不少排序算法要快得多)。spa

  之前在學排序算法的時候,專門有講怎樣根據實際問題來選擇合適的排序算法,可是我圖「省事」,就只用快排和簡單選擇排序。遇到了這個問題也讓我對算法的選擇和實現上有了更多認識,同時也瞭解到用棧消除遞歸在有些場合(好比系統棧空間受限)的重要意義。操作系統

相關文章
相關標籤/搜索