磁盘文件排序

......

BITMAPINFOHEADER bi;

bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = bmpScreen.bmBitsPixel; 
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0; 
bi.biClrUsed = 0; 
bi.biClrImportant = 0; 

DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount   31) / 32) * 4 * bmpScreen.bmHeight;  cBmpData = new unsigned char[dwBmpSize ]; 

GetDIBits(hdcScreen, hbmScreen, 0, (UINT)bmpScreen.bmHeight, cBmpData, (BITMAPINFO *)&bi, DIB_RGB_COLORS); 

DeleteObject(bmpScreen); 

ReleaseDC(hdcScreen); 

return cBmpData; 
} <---运行到这里时提示堆栈损坏

磁盘文件排序
问题描述,来自《编程珠玑》:
输入:一个最多含有n个不相同的正整数的文件,其中每个数都小于等于n,且n=10^7。
输出:得到按从小到大升序排列的包含所有输入的整数的列表。
条件:最多有大约1MB的内存空间可用,但磁盘空间足够。且要求运行时间在5分钟以下,10秒为最佳结果。

这是因为其实GetDIBits的第五个参数需要的其实是一个BITMAPINFO结构,而我们传入的是BITMAPINFOHEADER。

分析:

如果在位图不小于16位时,这是可行的。但是在位图小于16位时,它还需要另外的内存空间来储存一个调色板数据,所以就会发生堆栈损坏的错误。

1、归并排序。你可能会想到把磁盘文件进行归并排序,但题目要求中,你只有1MB的内存空间可用,所以,归并排序这个方法不行。

正确的做法是这样的

2、位图方案。例如正如《编程珠玑》一书上所述,用一个20位长的位字符串来表示一个所有元素都小于20的简单的非负整数集合,边框用如下字符串来表示集合{1,2,3,5,8,13}:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
美洲杯赌球,上述集合中各数对应的位置则置1,没有对应的数的位置则置0。

struct { BITMAPINFO info; RGBQUAD moreColors[255]; } fbi;
BITMAPINFOHEADER &bi = fbi.info.bmiHeader;
bi.biSize = sizeof(BITMAPINFOHEADER);
...
GetDIBits(..., &fbi.info, ...);

参考《编程珠玑》一书上的位图方案,针对10^7个数据量的磁盘文件排序问题,可以这么考虑,由于每个7位十进制整数表示一个小于1000万的整数。可以使用一个具有1000万个位的字符串来表示这个文件,其中,当且仅当整数i在文件中存在时,第i位为1。采取这个位图的方案是因为我们面对的这个问题的特殊性:1、输入数据限制在相对较小的范围内,2、数据没有重复,3、其中的每条记录都是单一的正整数,没有任何其它与之关联的数据。
所以,此问题用位图的方案分为以下三步进行解决:
第一步,将所有的位都置为0,从而将集合初始化为空。
第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。
第三步,检验每一位,如果该位为1,就输出对应的整数。
经过以上三步后,产生有序的输出文件。令n为位图向量中的位数(本例中为10000000),程序可以用伪代码表示如下:

 

//第一步,将所有的位都初始化为0   
for i ={0,....n}  
    bit[i]=0;  

//第二步,通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。   
for each i in the input file  
    bit[i]=1;  

//第三步,检验每一位,如果该位为1,就输出对应的整数。   
for i={0...n}  
    if bit[i]==1  
      write i on the output file  

不过很快,我们就将意识到,用此位图方法,严格说来还是不太行,空间消耗10^7/8还是大于1M(1M=1024*1024空间,小于10^7/8)。
既然如果用位图方案的话,我们需要约1.25MB(若每条记录是8位的正整数的话,则10000000/(1024*1024*8) ~= 1.2M)的空间,而现在只有1MB的可用存储空间,那么究竟该作何处理呢?可以多次使用位图进行排序。

3、多路归并。把这个文件分为若干大小的几块,然后分别对每一块进行排序,最后完成整个过程的排序。k趟算法可以在kn的时间开销内和n/k的空间开销内完成对最多n个小于n的无重复正整数的排序。比如可分为2块(k=2,1趟反正占用的内存只有1.25/2=0.625M),1~4999999,和5000000~9999999先遍历一趟,处理1~4999999的数据块(用5000000/8=625000个字的存储空间来排序0~4999999之间的整数),然后再第二趟,对5000001~1000000这一数据块处理。

针对这个要分两趟给磁盘文件排序的具体问题编写完整代码,如下:

美洲杯赌球 1美洲杯赌球 2View Code

//copyright@ yansha     
//July、2010.05.30。     
//位图方案解决10^7个数据量的文件的排序问题     
//如果有重复的数据,那么只能显示其中一个 其他的将被忽略     
#include <iostream>     
#include <bitset>     
#include <assert.h>     
#include <time.h>     
using namespace std;    

const int max_each_scan = 5000000;    

int main()    
{    
    clock_t begin = clock();    
    bitset<max_each_scan> bit_map;    
    bit_map.reset();    

    // open the file with the unsorted data     
    FILE *fp_unsort_file = fopen("data.txt", "r");    
    assert(fp_unsort_file);    
    int num;    

    // the first time scan to sort the data between 0 - 4999999     
    while (fscanf(fp_unsort_file, "%d ", &num) != EOF)    
    {    
        if (num < max_each_scan)    
            bit_map.set(num, 1);    
    }    

    FILE *fp_sort_file = fopen("sort.txt", "w");    
    assert(fp_sort_file);    
    int i;    

    // write the sorted data into file     
    for (i = 0; i < max_each_scan; i  )    
    {    
        if (bit_map[i] == 1)    
            fprintf(fp_sort_file, "%d ", i);    
    }    

    // the second time scan to sort the data between 5000000 - 9999999     
    int result = fseek(fp_unsort_file, 0, SEEK_SET);    
    if (result)    
        cout << "fseek failed!" << endl;    
    else    
    {    
        bit_map.reset();    
        while (fscanf(fp_unsort_file, "%d ", &num) != EOF)    
        {    
            if (num >= max_each_scan && num < 10000000)    
            {    
                num -= max_each_scan;    
                bit_map.set(num, 1);    
            }    
        }    
        for (i = 0; i < max_each_scan; i  )    
        {    
            if (bit_map[i] == 1)    
                fprintf(fp_sort_file, "%d ", i   max_each_scan);    
        }    
    }    

    clock_t end = clock();    
    cout<<"用位图的方法,耗时:"<<endl;    
    cout << (end - begin) / CLK_TCK << "s" << endl;    
    fclose(fp_sort_file);    
    fclose(fp_unsort_file);    
    return 0;    
}    

上述的位图方案,共需要扫描输入数据两次,具体执行步骤如下:
第一次,只处理1—4999999之间的数据,这些数都是小于5000000的,对这些数进行位图排序,只需要约5000000/8=625000Byte,也就是0.625M,排序后输出。
第二次,扫描输入文件时,只处理4999999-10000000的数据项,也只需要0.625M(可以使用第一次处理申请的内存)。因此,总共也只需要0.625M。

本文由美洲杯赌球发布于计算机教程,转载请注明出处:磁盘文件排序

TAG标签: 美洲杯赌球
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。