在《編程珠璣》一書裡提到了一種演算法導論裡沒有提到過的位元影像排序方法,這種排序方法是通過犧牲空間效率來追求時間效率(線性時間)以達到時間-空間折中與雙贏的目的。本文以執行個體形式簡單講一下位元影像排序思想。
一、問題描述
1.輸入:一個至多包含1千萬個非負整數的檔案
2.特徵:①每個數都是小於10000000的非負整數;②沒有重複的數字;③資料之間不存在關聯關係。
3.約束:①最多1MB的記憶體空間可用;②磁碟空間充足;③已耗用時間最多幾分鐘,最好是線性時間。
4.輸出:按升序排列的整數序列。
二、位元影像排序思想
由於待排序的資料記錄較多,我們單純地使用常見的排序方法時間效率較低,已耗用時間會很長。而且記憶體空間有限(限制為1MB左右),所以我們不能同時把所有整數讀入記憶體(如果每個整數使用7個位元組來儲存,那麼1MB記憶體空間只能存大約143000個數字)。當然我們可以多次讀取輸入檔案,多次排序,但是更好的方案是使用位元影像排序,可以使用有限的1MB記憶體空間並只進行一趟排序。
1.根據待排序集合中最大的數,開闢一個位元組,用來表示待排序集合中的整數;
2.待排序集合中的數字在位元組中的對應位置置1,其他的置0;
例如,待排序集合{1,2,3,5,8,13}可以表示為:0-1-1-1-0-1-0-0-1-0-0-0-0-1
這樣排序過程自然可以分為三步:
第一步:將所有的位都置為0;
第二步:通過讀入檔案中的每個整數,將每個對應的位都置為1;
第三步:檢驗每一位,如果該位為1,輸出對應的整數。
注意:位元影像排序是使用一個二進位位而不是一個整數來表示0或1,這樣可以大大地減少所需要的記憶體空間。使用位元影像排序的前提是要知道待排序序列中的最大數。位元影像排序的缺點是有些數沒有出現過,仍要為其保留一個位。故位元影像排序比較適合關鍵字密集的序列,例如一個城市的電話號碼。
虛擬碼如下:
/*Phase 1: initialize set to empty*/ for i = [0, n) bit[i] = 0 /*Phase 2: insert present elements into the set*/ for each i in the input file bit[i] = 1 /*Phase 3: write sorted output*/ for i = [0, n) if bit[i] == 1 write i on the output file
效能:時間複雜度可達O(n),1MB包含8*1024*1024個位,所需記憶體10000000/(8*1024*1024)=1.20MB,如果不是嚴格限制的話可以看做基本符合要求。
三、位元影像排序實現
位元影像排序時,我們需要考慮:給出一個數,如何找到其對應位元影像的位置,方法就是首先找到該數對應的位元組,然後在找到該數對應的位。例如:
unsigned char bitmap[2]; /* 可以表示16個數,即0~15 */
一個位元組有八位,5表示第0個位元組的第5位上;14表示第1個位元組的第6個位上。
在這裡為了簡化位處理,我們使用C++標準庫的bitset容器。bitset是C++提供的一種位集合的資料結構,它讓我們可以像使用數組一樣使用位,可以訪問指定下標的bit位。和其他容器一樣,bitset也是一個模板類。具體的bitset方法可以查看std::bitset reference。
下面我們使用bitset容器進行位元影像排序:
/************************************************************************* > File Name: BitSort.cpp > Author: SongLee ************************************************************************/ #include<bitset> #include<iostream> using namespace std; #define MAX 20 int main() { int arr[10] = {5,1,2,13,7,10,0,20,16,9}; bitset<MAX+1> bit; /* 將對應位置置1 */ for(int i=0; i<10; ++i) { bit.set(arr[i]); /* bit.set(n)表示將第n位置1 */ } /* 輸出排序結果 */ for(int i=0; i<MAX+1; ++i) { /* bit.test(n)判斷第n位是否為1 */ if(bit.test(i)) { cout << i << " "; } } cout << endl; }
輸出結果:0 1 2 5 7 9 10 13 16 20