問題描述
輸入:一個最多包含n個正整數的文件,每個數都小於n,其中n=107。如果在輸入文件中有任何正數重復出現就是致命錯誤。沒有其他數據與該正數相關聯。
輸出:按升序排列的輸入正數的列表。
約束:最多有1MB的內存空間可用,有充足的磁盤存儲空間可用。運行時間最多幾分鍾,運行時間為10秒就不需要進一步優化。
程序設計與實現概要:
應用位圖或位向量表示集合。可用一個10位長的字符串來表示一個所有元素都小於10的簡單的非負整數集合,例如,可以用如下字符串表示集合{1,2,4,5,8}:
0 1 1 1 0 1 0 0 1 0 0
代表集合中數值的位都置為1,其他左所有的位置為0.編程珠璣當中建議是一年個一個具有1000萬個位的字符串來表示這個文件,那么這個文件的所占容量為10000000 bit=107bit,不到1MB的大小,其中,當且精當整數i在文件中存在,第i為1,這個表示利用了該問題的三個在排序問題中不常見的屬性:輸入數據限制在相對較小的范圍內;數據沒有重復;而且對於每條記錄而言,除了單一個整數外沒有其他關聯數據。
如給定表示文件中整數集合的位圖數據結構,則可以分三個階段來編寫程序
第一階段:將所有的位都置為0,從而將集合初始化為空。
第二階段:通過讀入文件中的每個整數來建立集合,將每個對應的位置都置為1。
第三階段:檢驗每一位,如果該為為1,就輸出對應的整數,有此產生有序的輸出文件。
下面的C語言的實現和C++的實現代碼
C語言:
所申請的int數組如下所示:
字節位置=數據/32;(采用位運算即右移5位)
位位置=數據%32;(采用位運算即跟0X1F進行與操作)。
#include <stdio.h> #define MAX 10000000 #define SHIFT 5 #define MASK 0x1F #define DIGITS 32 int a[1+MAX/DIGITS]; void set(int n) //將邏輯位置為n的二進制位置為1 { a[n>>SHIFT] |=(1<<(n&MASK)); //n>>SHIFT右移5位相當於除以32求算字節位置,n&MASK相當於對32取余即求位位置, } void clear(int n) { a[n>>SHIFT] &=(~(1<<(n&MASK))); //將邏輯位置為n的二進制位置為0 } int test(int n) { return a[n>>SHIFT] & (1<<(n&MASK)); //測試邏輯位置為n的二進制位是否為1 } int main(int argc, char *argv[]) { int i,n; for(i=1;i<=MAX;i++) { clear(i); } while(scanf("%d",&n)!=EOF) { set(n); } for(i=1;i<=MAX;i++) { if(test(i)) printf("%d ",i); } return 0; }
C++(使用bitset)
#include <iostream> #include<bitset> using namespace std; int main(int argc, char *argv[]) { const int max = 10000000; int n,i; bitset<max+1> bit; //初始默認所有二進制位為0 while(scanf("%d",&n)!=EOF) { bit.set(n,1); //將第n位置1 } for(i=0;i<=max+1;i++) { if(bit[i]==1) printf("%d ",i); } return 0; }
---------------------------------------------------