zoukankan      html  css  js  c++  java
  • 《编程珠玑》读书笔记1

    位图数据结构法

    在“开篇”的里,讲述了排序的一个问题,大意就是,对一个“最多占n位的(就是n位的整数),随机的,无重复的(互异无序)”的整数序列进行排序,那么这个序列的总长度len<=10^n。例如:这个序列中的每个整数最多占3位,那么序列最多有0~999的1000个数。

    无重复会有很大的启发,可以试着使用“位图数据结构”来解决。位图数据结构?位图中的每个像素都被存储在计算机当中,并用一定的字节数来标记它的属性值。启发:如果是黑白的位图,那么位图中的每一个像素都可以用一个bool值来标记,因为位图无非就是黑与白,同理,可以应用到这个问题当中。

    如果出现了,就用‘1’来标记;反之,用‘0’来标记,而用一个数组来表示给出的序列,那么数组的下标就是对应的出现的整数了。例如:dl[1]=1,那么,1在这个序列中有出现。这就是在统计出现与否的同时达到了排序了效果,而且节省了大量的时间与空间。这个效果与用排序模板和经常使用的外部(归并)排序比起来是绝对胜出的。

    这是它“神奇”的地方,输入序列和输出序列都只有一次。1kw个上述整数序列也只用到了大约1.25M的内存空间。只能说,“位图数据结构法”在解决这样的问题有优势,只要把条件做小小的改动,情况会变得非常糟糕:把“互异”改为“非互异”,“位图法”无法胜任,还是要回到原始的外部排序算法。居然这样,那就一窥芳容。

    多路归并排序 

    外部排序,也就是借助了磁盘,所有的排序过程并不是都在内存中完成。所以,外部排序没什么可怕的,常用的外部排序就是多路归并排序,它归并排序是一样的。关于多路归并排序,在多路归并排序这篇博文当中写得很清楚,不罗嗦了,这篇博文写的很认真,这篇博文当是自己的学习笔记。

    珠玑“开篇”的课后习题第3题所要解决的就是测试多路归并排序的首要问题,所以没了它,也是无米之炊。产生随机数可以用rand库函数,但是它有缺陷的,rand函数产生的随机数最大是32767,但是这里需要KW,甚至更大的数据。 
    解决方法原理:可以依据“洗牌原理”,一副牌,将其中一张(任意挑选)与另一张(任意挑选)置换,...如此重复n次。牌没有增减,但是顺序打乱。

    于是有下面的random代码:

    //range:范围 
    //num:个数 
    void random(int range,int num) 
    { 
        int * a = new int[range],i,j;
        srand(unsigned(time(NULL)));
        for(i=0; i<range; i++) 
            a[i] = i + 1;
        ....核心代码去哪里啦!
        //    写入文件 
        for(i=0; i<num; i++) 
            cout << a[i] << " ";
        //    回收 
        delete [] a; 
    }

    有了上面的基础,操刀就容易了。有下面的图,多路归并也就浮出水面!
    image 

    其实,数据结构课程中的归并排序就把原始数据分成了2个分队(二路),并且它的所有工作只在内存中完成,不借助磁盘,另外二路更多采用递归算法的。上图举例:1kw的个整数,每个整数4B,规定内存只有1M,那么每次读入内存(1MB/4B)=250k个整数,所以有(1kw/250k=40)个分队,下面称为“段文件”。

    多路归并排序编程实现细节

    • 内存排序环节:磁盘中的数据序列被分割成多个段(假定内存有限)读入到内存中,在内存中用模板sort实现排序过程,效率高! 
    • 多路归并排序环节:依次从从每个已排序的段文件中(什么是段文件,看上面的内存排序环节,形象了点!!)读入一个数据,注意是一个;挑选最小的写入的目标文件中。

    数据的准备上面的random函数可以完全可以胜任。我测试的时候只准备了只有100w的数据,已经通过测试;小数据也可以通过,只是时间上有差异。

     

    下面是代码
    #include <iostream>
    #include <string.h>
    #include <fstream>
    #include <Algorithm>
    #include <time.h>
    using namespace std;
    
    #define MAX 10000                //    总数据量,可修改
    #define MAX_ONCE 2500        //    内存排序MAX_ONCE个数据
    #define FILENAME_LEN 20    
    
    //range:范围
    //num:个数
    void random(int range,int num)
    {
        int * a = new int[range],i,j;
        fstream dist_file;
    
        srand(unsigned(time(NULL)));
    
        for(i=0; i<range; i++)
            a[i] = i + 1;
    
        //    打表预处理
        for(j=0; j<range; j++)
        {
            //    rand函数产生的随机数最大是32767,不能直接调用rand,做一下处理
            int ii = (rand()*RAND_MAX+rand()) % range,        
                jj = (rand()*RAND_MAX+rand()) % range;
            swap(a[ii],a[jj]);
        }// for
    
        dist_file.open("data.txt",ios::out);
    
        //    写入文件
        for(i=0; i<num; i++)
            dist_file << a[i] << " ";
    
        //    回收
        delete [] a;
        dist_file.close();
    }
    
    bool cmp(int &a,int &b)
    {return a<b;}
    
    //index:文件的下标
    char * create_filename(int index)
    {
        char * a = new char[FILENAME_LEN];
        sprintf(a,"data %d.txt",index);
        return a;
    }
    
    //num:每次读入内存的数据量
    void mem_sort(int num)
    {
        fstream fs("data.txt",ios::in);
        int temp[MAX_ONCE],    //    内存数据暂存
            file_index = 0,            //    文件下标
            i,
            cnt;                            //    实际读入内存数据量
    
        bool eof_flag = false;    //    文件末尾标识
    
        while(!fs.eof())
        {
            for(i=0,cnt = 0; i<MAX_ONCE; i++)
            {
                fs >> temp[cnt];
    
                //    读入一个数据后进行判断是否到了末尾
                if(fs.peek()==EOF)    
                {
                    eof_flag = true;
                    break;
                }// if
    
                cnt++;
            }// for
    
            if(eof_flag)
                break;
    
            //    内存排序
            sort(temp,temp+cnt,cmp);
    
            char * filename = create_filename(++file_index);
            fstream fs_temp(filename,ios::out);
    
            //    写入
            for(i=0; i<cnt; i++)
                fs_temp << temp[i] << " ";
    
            fs_temp.close();
            delete [] filename;
        }// while
    
        fs.close();
    }
    
    void merge_sort(int filecnt)
    {
        fstream * fs = new fstream[filecnt],ret("ret.txt",ios::out);
        int index = 1,temp[MAX_ONCE],eofcnt = 0;
        bool * eof_flag = new bool[filecnt];
    
        ::memset(eof_flag,false,filecnt*sizeof(bool));
    
        for(int i=0; i<filecnt; i++)
            fs[i].open(create_filename(index++),ios::in);
    
        for(int i=0; i<filecnt; i++)
            fs[i] >> temp[i];
    
        while(eofcnt<filecnt)
        {    
            int j = 0;
            //    找到第一个未结束处理的文件
            while(eof_flag[j])
                j++;
    
            int min = temp[j],fileindex = 0;
            for(int i=j+1; i<filecnt; i++)
            {
                if(temp[i]<min && !eof_flag[i])
                    min = temp[i],fileindex = i;
            }// for
    
            ret << min << " ";
            fs[fileindex] >> temp[fileindex];
            //    末尾判断
            if(fs[fileindex].peek()==EOF)
                eof_flag[fileindex] = true,
                eofcnt++;
        }// while
    
        delete [] fs;
        delete [] eof_flag;
        ret.close();
    }
    
    int main()
    {
        random(MAX,MAX);
        clock_t begin = clock();
        mem_sort(MAX);
        merge_sort(4);
        clock_t end = clock();
    
        double cost = (end - begin)*1.0 / CLK_TCK;
        cout << "耗时:" << cost << "ms" << endl;
    }
  • 相关阅读:
    PHP之路——MySql基础操作语句
    PHP简单获取数据库查询结果并返回JSON
    iOS 写入文件保存数据的方式
    触摸事件
    UI基础
    UI基础
    UI基础
    VBS读取txt文档数据查找Excel中单元格数据符合条件的剪切到工作表2中
    vbs查找Excel中的Sheet2工作表是否存在不存在新建
    VBS操作Excel常见方法
  • 原文地址:https://www.cnblogs.com/zql-42/p/14941438.html
Copyright © 2011-2022 走看看