需要对数据进行分析处理,数据特征如下:
---------------------------------------------
1、每条数据Record有一个ID标识
2、不同数据record之间通过这个ID关联,但是ID关系并不是对应相等,也就是说组合后的数据ID并不是唯一的。
3、数据之间的关系通过两个ID值关联起来,比如ID1+ID2可以唯一确定一组有关系的数据。也就是说这组数据内的每条数据的ID满足如下关系:ID = ID1或者ID = ID2
4、数据处理的开始确定ID1和ID2,要求查找满足要求的数据。判决条件就是 ID = ID1 || ID = ID2给定的ID值对很多,需要设计一个数据结构实现快速的查找。请各位大侠帮忙设计设计,什么样的数据结构以及算法可以较好的实现这个功能呢?

解决方案 »

  1.   


    组合后的记录有很多,比如已经处理好一组数据group 1:rec1、rec2、rec3 。 
    其中rec1.id = 1 rec2.id = 2 rec3.id = 1 rec4.id = 2 ......
      

  2.   

    也即是说一组(group)内数据record有2个ID组成。现在要做的就是把别人给你的无序数据分组:group1、group2 。。group N
      

  3.   

    你的意思是微软的那套标志差不多的样子吧,标志位叠加那样的?MB_XX 1
    MB_ee 2
    MB_UU 4
    MB_OO 8
    .
    .
    .利用二进制的位实现标志?
      

  4.   

    俺也冒汗啊。举个例子吧:
    GROUD#01: REC1,REC2,REC3,REC4,...
    这样的一组数据,REC1.ID=0X01, REC2.ID=0X02,REC3.ID=0X01,REC4.ID=0X01可以看到这样的一组数据他的特征是ID=0X01或者ID=0X02,这时就说明ID(0X01,0X02)为这组数据的ID对,ID=0X01可以关联起来一部分数据,ID=0X02可以关联一部分数据,而又知道ID=0X01和ID=0X02是同属一个组,所以就可以实现全部记录的关联了。这里我考虑能否设计一个hash_map类似的容器,比如通过id=0x01,id=0x02作为key可以索引到相同的node,这样后续的rec都可以根据id进入自己的group了。请各位xd帮忙设计一个算法与数据结构吧。
      

  5.   

    你的意思是在一个数据集中查找符合ID=id1或ID=id2的所有记录?为何不用数据库?
      

  6.   

    你的问题是要如何设计文件来储存数据以便根据ID来查找数据是吗?
    每次查找时,总数据量大约有多少?ID的取值范围有多大?每条记录的长度是否固定、大约是多少?
      

  7.   

    不是文件设计问题。是数据处理与算法问题,我要从原始数据里面按照上述规则进行分组整理。每次处理的数据当中有可能有几十万个group。ID的取值为short类型数据。从原来随机的数据当中我需要按照规则进行整理分组统计。所以需要设计一个类似的map来使用这些检查出来的ID建立索引,高效是最高要求。
      

  8.   

    还是没搞清楚,看看是不是这样?
    现在有一个文件,里面有很多Record,每个Record有一个不唯一的ID,ID取值范围是0~655536,Record的长度不固定,无序存放。要做的是先建立一个索引,以便后面任意给出一对ID就能快速地找出这一对ID对应的所有Record(这些Record合在一起作为一个group)。
    如果上面的理解没错,还需要知道Record的大约数量及总数据量的多少。
      

  9.   


    基本没错!只不过不是给出一个ID,而是你自己从这对文件里面自行解析出来每一个ID,然后根据ID把后续的属于一个ID pair的记录组合到一起。record的数量从是百万到上千万都有。总数据量大概上TB的量吧。总结来讲:给你一堆文件TB级大小,上千万记录,每个记录有一个ID标识,ID = 0~65535,根据ID把所有的记录分组GROUP,每一组数据都有开始BEGIN、结束END标识,但是在一定不同组相同的ID不会在同一个时间段内出现,所以处理的时候首先得到begin,到end之间出现的ID都肯定是该group的记录。处理结果:TB文件,几千万记录,最后按照ID被分成几十万个、几百万个group,每个group内有自己的记录rec。需要做的:建立数据结构保存各组rec索引,利用解析到的每个记录的ID结合begin、end起止信息建立索引机制,利用ID作为索引信息定位分组后续的每一个记录数据(ID = IDx),每一组数据有一个ID PAIR标识GROUP
      

  10.   

    “但是在一定不同组相同的ID不会在同一个时间段内出现”这句没有理解。
    还有,为什么要用一对ID来标识一个group?这一对ID如何确定?不可能把任意两个ID都搞一个group吧?
      

  11.   


    不好意思,打字乱掉了。是这样的:
    一个group数据是由一个ID值对(ID1,ID2)唯一标识的,比如说记录双方设计到两个人,每方一个ID标识,每个group标识begin的那条记录是可以获取到这个ID值对的(ID1,ID2),就好像告诉你以下这两个人是一个组,他们后续各自的所有记录是属于一个group的。由于ID总归会重复使用,但是在同一个时间段内,所有group的ID值对肯定是互不相同的。比如说,开始ID(ID1,ID2)标识GROUP1,但过一段时间这个ID(ID1,ID2)值对可能会被用来标识GROUP100了,当然可能它过一会又被用来标识其他的group了。不知道我有没用描述清楚,谢谢!
      

  12.   

    是不是可以理解为网络聊天记录?每个用户每次登录随机分配一个ID,每条Record中都包含两个ID值(一个源ID,一个目的ID),要从所有用户的聊天记录中把每一组对话信息提取出来?
    如果是这样,那么如何确定一组对话结束?
      

  13.   

    把ID1,ID2组成一个KEY(ID1_AND_ID2),放到MAP内.
      

  14.   


    类似的东西吧。对每一个记录处理时候会首先检查其rec.TYPE,如果REC.TYPE = BEGIN,则说明这个记录包含两个ID(ID1,ID2),这时就获取了GROUP的标识ID PAIR(ID1,ID2),后续的每个记录REC.TYPE = DATA,说明这个记录只有一个ID,要么ID = ID1,要么ID = ID2,直到遇到一个记录REC.TYPE = END,这个记录也包含两个ID,并且为ID PAIR(ID1,ID2),则说明这个GROUP可以个关闭了。所以总结来看:
    REC.TYPE = BEGIN, ID = ID pair(ID1,ID2) 开始
    REC.TYPE = DATA,  ID = ID1 or ID2       GROUP组内数据记录
    REC.TYPE = END    ID = ID pair(ID1,ID2) 结束谢谢。
      

  15.   

    基本上理解了,还有一点确认一下,你要做的是把混乱的Records整理成Groups,还是给已经整理好的Groups建立索引以便根据ID对来查找?or both?
      

  16.   

    先将数据文件排序吧,TB级的乱序文件,作起索引来,索引消耗内存也是灰常巨大滴。
    也就是楼上所说的整理一哈。
    我的意见是按每天记录的ID、时间什么排个序,然后针对有序的数据文件做索引。
    当然,有可能我没有确切理解楼主的意图。
    呵呵。
      

  17.   


    目的是把混乱的records整理成groups,便于后续的查找和统计使用。由于数据量太大,所以考虑效率多些。谢谢!
      

  18.   

    既然是整理数据,至少要把所有数据都读写一遍的,要高效就要设法读写一遍就搞定,不过这一点很简单,从头开始顺序读取数据,在处理过程中将整理好的group依次写入另一文件即可。
    在读写磁盘文件方面,要注意集中读、集中写,就是在读数据的过程中不要执行其它访问磁盘的操作,在写的过程中也不要执行其它访问磁盘的操作,每次连续读或连续写的数据量越多越好,在集中读或集中写的过程中可以对内存中的数据进行处理,但要尽量避免虚拟内存交换。这样可以尽量避免反复的磁盘寻道操作,提高效率。磁盘操作中最费时的就是寻道。另外提一下,很多人以为使用文件映射可以大大提高效率,其实不然,文件映射只是略过了磁盘缓冲和一些中间代码,在进行大量不重复的磁盘读写操作时,其节省的时间是忽略不计的,而且,如果使用不当,反而会大大降低效率,所以不建议使用。
    查找group时,可以通过哈希表快速定位。
    处理过程中,先将整理好的group保存在内存中,处理一定量的数据后,集中对groups进行一次保存,并更新哈希表。根据系统中物理内存资源的情况,充分利用物理内存,但要留出一些,以免物理内存不足时影响效率。
    整理过程中建立一个索引,记录ID对对应的group的位置,以便后续查找。
      

  19.   


    多谢!您说的非常有道理,这里我的问题就集中在这部分了。我也打算使用hash表进行处理,不过如何使用这里的ID PAIR(ID(n),ID(m))设计HASH_MAP呢?由于只有BEGIN/END record携带有ID PAIR,其他的组内记录只具有ID PAIR中的一个。还请大虾指点。谢谢!
      

  20.   

    每个ID在某一时间段内只能与一方对话是吗?
    如果这样就更简单了,不用hash table,用一个64K长的指针数组,指向每个ID当前对应的group信息结构。
      

  21.   


    对,一个时间段内的ID PAIR可以唯一的确定一个通话双方。比如:
    REC.TYPE = BEGIN,ID = ID PAIR(ID1,ID2)
    REC.TYPE = DATA , ID = ID1 or ID2
    REC.TYPE = END ,ID = ID PAIR(ID,ID2)在BEGIN ... END之间,这个ID PAIR不可能被两个通话过程使用的。请指点!
      

  22.   

    定义一个结构体记录正在整理的每个group的信息。
    分配一个64K长的指针数组,以ID值作为数组下标,每个元素指向该ID当前对应的group信息结构,整个数组初始化为NULL。
    begin的时候分配group结构,将结构指针写入数组(ID1、ID2对应的两个元素)。
    end的时候清除数组中对应的两个指针。
      

  23.   

    // 定义结构体
    typedef struct GROUP_DATA
    {
    int id1;
    int id2;
    int name;
    int source;
    int dest;   
    } MY_GROUP_DATA,*PMY_GROUP_DATA;
    // 声明指针数组
    PMY_GROUP_DATA MyGroups[65535];
    memset(MyGroups,sizeof(MY_GROUP_DATA),0);
    //
    while(!BeEnd)
    {
    GetData();
    ParseData();
    if(rec.type == BEGIN)
      createData();
    else if (rec.type == DATA)
      appendData();
    else if (rec.type == END)
      deleteData();
    }
    //
    createNew()
    {
    PMY_GROUP_DATA pData = new MY_GROUP_DATA pData();
    MyGroups[rec.id1] = pData;
    MyGroups[rec.id2] = pData;
    }
    //
    appendData()
    //
    deleteData()
    {
    MyGroups[rec.id1] = NULL;
    MyGroups[rec.id2] = NULL;
    }
    大致这个思路是么?这么实现和使用hash_map(int,MY_GROUP_DATA pData)比较如何?
      

  24.   

    不好意思,这里写错了!
    memset(MyGroups,sizeof(MY_GROUP_DATA),0);更改为
    memset(MyGroups,sizeof(int),0);
      

  25.   

    大致是这个样子。
    这种方法比用hash要快,但浪费储存空间,不过你现在处理的问题只需要sizeof(void*)*0x10000字节,几百KB而已,所以不用在意空间问题。
      

  26.   

    hash_map 同样会遇到两个ID对应一个节点数据的情况,如果每次进行删除或者更新需要查找两次map表。看起来应该没有这个快。不过,在处理的过程中,还需要用到超时控制机制,这个是不是会对处理效率产生不的影响?我的意思是说,不是每个通话的过程都一定有rec.type == END出现,比如异常终止的通话,只有BEGIN....DATA....没有END出现,这个时候我需要根据超时控制来强制结束这个会话,比如超时时间设定为5min,那么这个时候我就要扫描每一个节点,遍历MyGroups[65535],检查它的节点数据的时间信息,查看是否超时,如果超时强制关闭这个会话。再结合这点要求,如何处理为好?多谢!我先照这个思路实现。有问题再请教,通过和您的讨论,受益匪浅。非常感谢!
      

  27.   

    定时扫描会影响整体效率,对于没有END的情况,可以考虑这样处理:每次遇到BEGIN的时候,先分别检查这两个ID的上次会话是否结束(对应的指针是否为空),如果没有结束则先将其结束。整个整理过程完成时再做一次扫描,把所有绘画都结束。