如何写出优美的代码(三)
(该文思想来自于经典著作《编程珠玑》)
看到有朋友评论说,美的代码不仅仅是规范上面的事。规范的代码可以让我们减少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; }
这个例子主要证明了正确理解问题的重要性。明确理解问题,针对特定条件去思考,是写出优雅算法的第一步。这里用的位图的技术非常简洁,原作者引用的一句话给我很大启发:“设计者确定其设计达到了完美的标准不是不能再增加任何东西,而是不能再减少任何东西。”