(該文思想來自於經典著作《編程珠璣》)
看到有朋友評論說,美的代碼不僅僅是規范上面的事。規范的代碼可以讓我們減少Debug的難度,增加可擴展性。當遇到性能問題的時候,我們就需要改進算法了。
編程珠璣的開篇提到了一個給最多一千萬個7位電話號碼排序的問題。首先我們看這個問題的常規解決方案:
C版本:
int intcomp(int *x,int *y){ return *x - *y } int a[10000000] int main(void){ int i,n = 0; while (scanf("%d", &a[n] != EOF) n++; qsort(a, n, sizeof(int), intcomp); for (i = 0; i < n; i++) printf("%d\n", a[i]); return 0; }
C++版本:
int main(void){ set<int> S; int i; set<int>::iterator j; while (cin >> i) S.insert(i); for (j = S.begin(); j != S.end(); ++j) cout << *j << "\n"; return 0; }
但是,條件有限制:可用內存只有1MB。我們看到這么小的內存,直接想法是在磁盤上歸並排序把。但是,進一步想,如果用32位整數存儲電話號碼,可以存250000個號碼。這樣,可以把號碼按大小順序分成40段,依次放到內存里快排。
再進一步分析該問題的特殊性:1)電話號碼沒有重復,2)數字小於1000萬,3)需要排序的僅僅是電話號碼,沒有其他關聯數據。由此想到,可以用一個1000萬個位的字符串表示這個文件,當且僅當整數i在文件中存在時,第i位為1。還可以用位圖表示集合,上代碼:
//32位整數 #define BITSPERWORD 32 #define SHIFT 5 #define MASK 0x1F #define N 10000000 int a[1 + N/BITSPERWORD]; void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); } void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); } int test(int i) { return a[i>>SHIFT] & (i>>(i & MASK)); } int main(void) { int i; for (i = 0; i< N; i++) clr(i); while (scanf("%d", &i) != EOF) set(i); for (i = 0; i < N; i++) if (test(i)) printf("%d\n", i); return 0; }
這個例子主要證明了正確理解問題的重要性。明確理解問題,針對特定條件去思考,是寫出優雅算法的第一步。這里用的位圖的技術非常簡潔,原作者引用的一句話給我很大啟發:“設計者確定其設計達到了完美的標准不是不能再增加任何東西,而是不能再減少任何東西。”