前言 :
當我們所要排序的資料量太大或檔案太大, 無法直接在記憶體內排序, 而須依賴外部儲存裝置時, 我們就會使用到外部排序法. 外部儲存裝置又可依照存取方式分為兩種, 如循序存取 (磁帶) 或隨機存取 (如磁碟). 要循序存取的檔案就像是串列一樣, 我們必須事先走訪整個串列才有辦法進行排序, 而隨機存取的檔案就像是陣列, 資料存取方便所以相對的排序也會比循序存取快一點, 一般來說外部排序法最常使用的就是合併排序法.
直接合併排序法 :
直接合併排序法 (Direct Merge Sort) 是外部儲存裝置最常用的排序方法, 它可以分成兩個步驟 :
例如 : 我們第一步把一個檔案分成6個小檔案 :
小檔案都完成排序後, 兩兩合併成一個較大的檔案, 最後再合併成一個檔案即可完成.
接著以一個實際範例說明, 假設我們要對一個檔案 test.txt 進行排序, 而 test.txt 裡包含 1500 筆資料, 但記憶體最多一次只能處理 300筆資料 :
步驟一 : 將 test.txt 分成五個檔案 t1~t5, 每個檔案300筆.
步驟二 : 以內部排序法對 t1~t5 進行排序.
步驟三 : 進行檔案 t1, t2 合併, 將記憶體分成三部分, 每部分可存放100筆資料, 先把 t1 及 t2 的前100筆資料放到記憶體裡, 排序後放到合併完成緩衝區, 等到緩衝區滿了之後便寫入磁碟 .
步驟四 : 重複步驟三直到完成為止.
合併的方法如下 :
假設我們有兩個完成排序的檔案要合併, 排序由小到大 :
首先在兩個檔案中分別讀出一個元素進行比較, 比較後將較小的檔案放入合併緩衝區內.
Step1 : 1與2 比較後將較小的1放進緩衝區, a1 的檔案指標往後一個元素.
Step2 : 2與4 比較後將較小的2放進緩衝區, b1 的檔案指標往後一個元素.
Step3 : 3與4 比較後將較小的3放進緩衝區, b1 的檔案指標往後一個元素.
Step4 : 4與5 比較後將較小的4放進緩衝區, a1 的檔案指標往後一個元素.
依此類推, 等到緩衝區的資料滿了就進行寫入檔案的動作; a1 或 b1 的檔案指標到了最後一筆就讀取剩下的資料完成排序.
範例代碼 :
執行結果 :
補充說明 :
* Algorithm Gossip: 合併排序法
當我們所要排序的資料量太大或檔案太大, 無法直接在記憶體內排序, 而須依賴外部儲存裝置時, 我們就會使用到外部排序法. 外部儲存裝置又可依照存取方式分為兩種, 如循序存取 (磁帶) 或隨機存取 (如磁碟). 要循序存取的檔案就像是串列一樣, 我們必須事先走訪整個串列才有辦法進行排序, 而隨機存取的檔案就像是陣列, 資料存取方便所以相對的排序也會比循序存取快一點, 一般來說外部排序法最常使用的就是合併排序法.
直接合併排序法 :
直接合併排序法 (Direct Merge Sort) 是外部儲存裝置最常用的排序方法, 它可以分成兩個步驟 :
例如 : 我們第一步把一個檔案分成6個小檔案 :
小檔案都完成排序後, 兩兩合併成一個較大的檔案, 最後再合併成一個檔案即可完成.
接著以一個實際範例說明, 假設我們要對一個檔案 test.txt 進行排序, 而 test.txt 裡包含 1500 筆資料, 但記憶體最多一次只能處理 300筆資料 :
步驟一 : 將 test.txt 分成五個檔案 t1~t5, 每個檔案300筆.
步驟二 : 以內部排序法對 t1~t5 進行排序.
步驟三 : 進行檔案 t1, t2 合併, 將記憶體分成三部分, 每部分可存放100筆資料, 先把 t1 及 t2 的前100筆資料放到記憶體裡, 排序後放到合併完成緩衝區, 等到緩衝區滿了之後便寫入磁碟 .
步驟四 : 重複步驟三直到完成為止.
合併的方法如下 :
假設我們有兩個完成排序的檔案要合併, 排序由小到大 :
首先在兩個檔案中分別讀出一個元素進行比較, 比較後將較小的檔案放入合併緩衝區內.
Step1 : 1與2 比較後將較小的1放進緩衝區, a1 的檔案指標往後一個元素.
Step2 : 2與4 比較後將較小的2放進緩衝區, b1 的檔案指標往後一個元素.
Step3 : 3與4 比較後將較小的3放進緩衝區, b1 的檔案指標往後一個元素.
Step4 : 4與5 比較後將較小的4放進緩衝區, a1 的檔案指標往後一個元素.
依此類推, 等到緩衝區的資料滿了就進行寫入檔案的動作; a1 或 b1 的檔案指標到了最後一筆就讀取剩下的資料完成排序.
範例代碼 :
- #include
- #include
- #include
- #include
- using namespace std;
- void mergeSort(ofstream& fp, ifstream& fp1, ifstream& fp2) {
- char n1,n2;
- char txt1[40];
- char txt2[40];
- fp1>>n1;
- fp2>>n2;
- while(fp1.eof()==0 && fp2.eof()==0) {
- if(n1 <= n2) {
- fp.put(n1);
- fp1 >> n1; /*繼續下筆 n1資料*/
- } else {
- fp.put(n2);
- fp2 >> n2; /*繼續下筆 n2資料*/
- }
- }
- if(fp1.eof()) {
- while(!fp2.eof()) {
- fp2>>n2;
- fp.put(n2);
- }
- } else if(fp2.eof()) {
- while(!fp1.eof()) {
- fp1 >> n1;
- fp.put(n1);
- }
- }
- }
- void main() {
- char *data1Path = "E:/MS-VSProj/DataStructureExam/Data/data1.txt";
- char *data2Path = "E:/MS-VSProj/DataStructureExam/Data/data2.txt";
- char *dataPath = "E:/MS-VSProj/DataStructureExam/Data/data.txt";
- char txt[40];
- ofstream fp;
- ifstream fp1, fp2;
- ifstream f,f1,f2;
- fp.open(dataPath, ios::out);
- fp1.open(data1Path, ios::in);
- fp2.open(data2Path, ios::in);
- if(!fp.is_open()) {
- cout << "開啟檔案 data.txt 失敗!" << endl;
- } else if(!fp1.is_open()) {
- cout << "開啟檔案 data1.txt 失敗!" << endl;
- } else if(!fp2.is_open()) {
- cout << "開啟檔案 data2.txt 失敗!" << endl;
- } else {
- cout << "資料排序中..." << endl;
- mergeSort(fp, fp1, fp2);
- cout << "資料排序完成!!" << endl;
- }
- fp.close();
- fp1.close();
- fp2.close();
- cout << "data1.txt 資料內容為: " << endl;
- f1.open(data1Path, ios::in);
- while(!f1.eof()) {
- f1 >> txt;
- cout << txt << endl;
- }
- cout << "data2.txt 資料內容為: " << endl;
- f2.open(data2Path, ios::in);
- while(!f2.eof()){
- f2 >> txt;
- cout << txt << endl;
- }
- cout << "排序後 data.txt 資料為: " << endl;
- f.open(dataPath, ios::out);
- while(!f.eof()) {
- f >> txt;
- cout << txt << endl;
- }
- f.close();
- f1.close();
- f2.close();
- }
補充說明 :
* Algorithm Gossip: 合併排序法
This message was edited 6 times. Last update was at 06/06/2010 13:44:27
沒有留言:
張貼留言