《編程珠璣》中「位圖排序」引起的一系列實驗

問題:linux

一個文件有8*106個正整數,每一個數都小於107。文件中全部整數都是惟一的。要求對這些整數排序,按升序把排序的結構輸出到磁盤上。ios

 

解決問題的方案有不少,下面咱們介紹幾種典型的方案。數組

 

一、高效的「位圖排序」函數

特殊要求:最多有大約1M的內存空間可用!測試

若是在這個限制下,快速的對這些整數進行排序,一個優秀的解決方案是使用「位圖排序」。spa

位圖排序的思想就是在內存中申請一塊連續的空間做爲位圖,初始時將位圖的每一位都置爲0。而後依次讀取待排序的整數,將整數對應的位設置爲1。最後按順序掃描位圖,若是某一位爲1,輸出到已排序文件。命令行

好比待排序的數據S={3,0,4,1,7,2,5},最大爲7,咱們能夠設置一個八位的位圖B,將位圖的每一位初始爲0。對S中的每個整數d,設置B[d]=1,即B=[1,1,1,1,1,1,0,1],最後掃描位圖,對位圖的每一位i,若是B[i]==1,則輸出i到已排序文件,排序後的S={0,1,2,3,4,5,7}。指針

整個過程只須要遍歷一遍待排序文件和位圖,時間複雜度O(n),須要的輔助空間爲(max(S)/8)字節。code

程序代碼:blog

bitmap_sort.c

#include <stdio.h>
#include "data_size.h"

#define SHIFT 5
#define MASK 0x1F

int a[1+MAX_DATA/32];

void set(int i)
{
    a[i>>SHIFT] |=1<<(i& MASK );
}

int test(int i)
{
    return a[i>>SHIFT] & (1<<(i& MASK ));
}

int main(int argc,char** argv)
{
    int i=0;
    FILE *fin=NULL,*fout=NULL;
    for(i=0;i<1+MAX_DATA/32;i++)
        a[i]=0;

    if((fin=fopen(argv[1],"r"))==NULL)
    {
        printf("error!\n");
        return 1;
    }
    while(fscanf(fin,"%d",&i)!=EOF)
        set(i);
    
    if((fout=fopen(argv[2],"w"))==NULL)
    {
        printf("error!\n");
        return 1;
    }
    for(i=0;i<MAX_DATA;i++)
        if(test(i))
            fprintf(fout,"%d\n",i);
    fclose(fin);
    fclose(fout);
    return 0;
}

data_size.h

#ifndef DATA_SIZE_H_
#define DATA_SIZE_H_

#define SIZE 8000000
#define MAX_DATA 10000000

#endif

生產待排序文件數據的程序見附錄。

 

測試(用time命令統計運行時間):(2次)

編譯以後的可執行程序:bitmapsort  待排序文件:data.txt  輸出文件:data2.txt

 

注:linux的time統計程序的運行時間,其統計結果包含如下數據:
1)實際時間(real time): 從command命令行開始執行到運行終止的消逝時間;
2)用戶CPU時間(user CPU time): 命令執行完成花費的用戶CPU時間,即命令在用戶態中執行時間總和;
3)系統CPU時間(system CPU time): 命令執行完成花費的系統CPU時間,即命令在覈心態中執行時間總和

 

二、linux排序命令

$ time sort -n -o data3.txt data.txt

使用linux的sort命令進行排序。

-n  :使用「純數字」進行排序(默認是以文字型態來排序的);

-o : 把排序結果輸出到文件,而不是控制檯

 

測試:(2次測試)

待排序文件:data.txt  輸出文件:data3.txt

 

三、C標準庫的qsort()函數

qsort包含在<stdlib.h>頭文件中,此函數根據你給的比較條件進行快速排序,經過指針移動實現排序。排序以後的結果仍然放在原數組中。使用qsort函數必須本身寫一個比較函數。

函數原型:

void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );

base :數組起始地址
num:數組元素個數
siz:每個元素的大小
comparator:函數指針,指向比較函數

qsort.c

#include <stdio.h>
#include <stdlib.h>
#include "data_size.h"

int intcomp(const void *x, const void *y)
{
    return *(int *)x-*(int *)y;
}

int a[SIZE];

int main(int argc, char** argv)
{
    FILE *fin=NULL,*fout=NULL;
    if((fin=fopen(argv[1],"r"))==NULL)
    {
        printf("open file error!\n");
        return 1;
    } 
    int i=0;
    while(fscanf(fin,"%d",&a[i])!=EOF)
        i++;
    qsort(a,SIZE,sizeof(int),intcomp);

    if((fout=fopen(argv[2],"w"))==NULL)
    {
        printf("open file error!\n");
        return 1;
    }
    for(i=0;i<SIZE;i++)
        fprintf(fout,"%d\n",a[i]);
    return 0; 
}

測試:(2次)

編譯以後的可執行程序:qsort  待排序文件:data.txt  輸出文件:data4.txt

 

四、C++標準庫中set容器來進行排序

由於待排序的整數都是惟一的,而且set容器內部會按照必定的順序組織內部的數據。咱們能夠用set容器來進行排序。

set_sort.cpp

#include <iostream>
#include <fstream>
#include <set>
using namespace std;

int main(int argc,char **argv)
{
    int i=0,n=0;
    set<int> s;
    set<int>::iterator it;
    ifstream in(argv[1]);
    if(!in.is_open())
    {
        cout<<"error"<<endl;
        return 1;
    }
    while(!in.eof())
    {
        in>>n;
        s.insert(n);
    }

    ofstream out(argv[2]);
    if(!out.is_open())
    {
        cout<<"error"<<endl;
        return 1;
    }
    for(it=s.begin();it!=s.end();++it)
    {
        out<<*it<<endl;
    }

    in.close();
    out.close();
    return 0;
}

測試:

編譯以後的可執行程序:set_sort  待排序文件:data.txt  輸出文件:data5.txt

 

總結

上面四個程序的排序結果都正確,已用diff命令對比過。

從上面的運行時間,咱們能夠看出:

位圖排序、C標準庫qsort(排序)、系統命令排序、C++標準庫set容器排序,它們的排序運行時間依次增高。

在特殊要求「最多有大約1M的內存空間可用!」的狀況下,位圖排序是一個很是優秀的排序方法,其運行速度快且佔用內存小,可是前提是,待排序的數據都是整數,且不存在重複。

 

附錄:

生產包含8*106個正整數 且 每一個數都小於107的排序文件。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "data_size.h"

int a[MAX_DATA];

void swap(int i,int j)
{
    int temp=a[j];
    a[j]=a[i];
    a[i]=temp;
}

int main(int argc, char **argv)
{
    int i=0;
    printf("%x\n",RAND_MAX);
    printf("%ld\n",time(NULL));
    srand(time(NULL));

    for(i=0;i<MAX_DATA;i++)
    {
        a[i]=i;
    }

    for(i=0;i<SIZE;i++)
    {
        int j=rand()%MAX_DATA;
        swap(i,j);
    }

    FILE * fin=NULL;
    if((fin=fopen(argv[1],"w"))==NULL)
    {
        printf("error!\n");
        return 1;
    }

    for(i=0;i<SIZE;i++)
    {
        fprintf(fin,"%d\n",a[i]);
    }
    fclose(fin);
    
    printf("success!\n");
    return 0;
}
相關文章
相關標籤/搜索