set,bitset 的一个应用实例——数据结构和比较算法

问题的来源: http://topic.csdn.net/u/20080415/10/a676aaa4-766e-4429-a86d-821f2e5ff775.html

问题描述:

有近30万个vector <int>(每个vector <int>中的值为0~179),如:
vector  < vector  < int >>  a;
a[
0 ] = {0,3,179} ;
a[
1 ] = {} ; // 该vector为空
a[ 2 ] = {2,6,8,56,119,36} ;
a[
3 ] = {03179} ;
a[
4 ] = {1,2,3,4,5,6,7,8,9,10...,179} ; //  “满”,表示从0到179间的数均在该vector中
// ....
a[ 310000 ] = {/*...*/};

每个vector <int>中是没有重复的数据的。即不可能出现类似a[n]={1,3,25,25,98}中有2个25的情况。
先要对上述31万个vector <int>进行压缩,压缩原则为
  1. 空vector <int>删除,如a[1]
  2. “满”vector <int>删除,如a[4]
  3. 相同vector <int>删除其中一个,如a[0]和a[3]相同,则删除其中任意一个
  4. 互补vector <int>删除其中一个,互补的意思是两vector <int>不相交且其并集为“满”vector <int>

问题分析:

对于问题所描述的数据,不难看出,不管是 a 的各个项还是 a[n] 的各个项,都具有唯一、无序(有顺序但不重要)的典型的集合的特征。或者说,问题的目的就是要把原来的数据转换为一个集合的集合。
这里需要注意的是,根据原则 4 ,0~179 的集合具有互补等价的关系。这一点,在设计算法的时候需要注意。
以变换代替原问题的压缩,是解决这个问题的主要思路。

数据的表示:

对于集合,可以用 STL 中的 set 来实现。而对于全集较小的 0~179 的集合,更恰当的方法是用非标准的 bitset 来实现。即用 180 位的 bitset 来表示0~179是否存在与集合中。
这样整个数据大体上是这样:
set <bitset<180>, CompBitSet>  a;
其中 CompBitSet 用于比较两个 bitset ,以决定顺序以及判断是否等价。

排序的问题:

由于0~179的集合具有互补等价的关系,在确定顺序和判断是否等价的时候,需要一些特别的处理。
bitset 内部是用 unsigned long 实现的,如果能充分利用这一点,无疑会大大提高比较判断的效率。
当 两个 bitset 进行比较的时候,利用互补等价的条件,将参与比较的两个 bitset 通过等价变换(补集)将特定位(比如第 0 位)转换为特定值。然后再把两个 bitset 进行高效率的数值比较(bitset具体是什么顺序并不重要,重要的是有序,不重复)。
具体的实现是:
// --------------------------------------
//  对两个 bitset 进行数值比较
template < size_t _N >
bool   operator   <  ( const  bitset < _N >   &  a,  const  bitset < _N >   &  b){
    size_t i;
    
for (i  =   0 ; i  <=  (_N  -   1 /   32  ;i ++ ){
        
if (a._W(i)  !=  b._W(i)) return  (a._W(i)  <  b._W(i));
    }
    
return   false ;
}

// --------------------------------------
//  用于 set 的比较类
struct  CompBitSet{
    
bool   operator  ()( const  itemset  & a,  const  itemset  & b) const {
        return (a[0] ? ~a : a) < (b[0] ? ~b : b);
    }
};

实测:

综合前面所述,我们已经把原来的数据压缩的问题转化成数据表示的问题。只要把原来的 vector<vector<int>> 转换为 set<bitset<180>, CompBitSet>,重复的数据就会自然消除。而且,在需要的时候,仍然可以用很小的代价很方便的重构 vector<vector<int>>。

由于没有原始的数据,我这里是用随机数生成的数据进行消除等价项、检查集合构建速度的测试:
/*
 * vector<vector<int>> 压缩
 
*/
#pragma  warning(disable:4786)
#include  < time.h >

#include 
< iostream >
#include 
< bitset >
#include 
< set >

#define  BITLEN 180

using  std::bitset;
using  std:: set ;
using  std::cout;
using  std::endl;

// --------------------------------------
struct  CompBitSet;

typedef bitset
< BITLEN >  itemset;
typedef 
set < itemset,CompBitSet >  dataset;

// --------------------------------------
template < size_t _N >
bool   operator   <  ( const  bitset < _N >   &  a,  const  bitset < _N >   &  b){
    size_t i;
    
for (i  =   0 ; i  <=  (_N  -   1 /   32  ;i ++ ){
        
if (a._W(i)  !=  b._W(i)) return  (a._W(i)  <  b._W(i));
    }
    
return   false ;
}

// --------------------------------------
struct  CompBitSet{
    
bool   operator  ()( const  itemset  & a,  const  itemset  & b) const {
        return (a[0] ? ~a : a) < (b[0] ? ~b : b);
    }
};

// --------------------------------------
void  listset( const  dataset  & data){
    dataset::const_iterator citer;
    
for (citer  =  data.begin();citer  !=  data.end();citer ++ ){
        
for ( int  i  =   0 ;i  <  BITLEN;i ++ ){
            
if (( * citer)[i])cout << i << ' , ' ;
        }
        cout
<< endl;
    }
}

// --------------------------------------
void  main( void ){
    dataset data;
    itemset item;

    srand((unsigned 
int )time(NULL));

    cout
<< " Start init data " << endl;
    system(
" pause " );
    
int  i,j;
    time_t begtime,usetime;
    begtime
=  time(NULL);
    
for (i  =   0 ;i  <   1000000 ;i ++ ){
        item.reset();
        
for (j  =   0 ;j  <  BITLEN;j ++ )
            item.
set (rand()  %  BITLEN);
        data.insert(item);
        data.insert(item);
        data.insert(
~ item);
        
if ( 0   ==  i  %   123 )cout << ' ' << i;
    }
    cout
<< " Times :  " << i << endl << " End of Init " << endl;
    cout
<< " Total items :  " << data.size() << endl;
    usetime 
=  time(NULL)  -  begtime;
    cout
<< " Use time :  " << usetime  /   60 << " : " << usetime  %   60 << " ( " << usetime << " s) " << endl;

    item.reset();
    data.insert(item);
    data.insert(
~ item);
    cout
<< " Insert empty set and full set " << endl;
    cout
<< " Total items :  " << data.size() << endl;

    data.erase(item);
    cout
<< " Remove empty set " << endl;
    cout
<< " Total items :  " << data.size() << endl;

//     listset(data);
}
实测的结果比较满意,在我的 Athlon 550M/256M 的机子上,生成百万数据集且每条数据项按各种方式执行3次插入,实测用时5:27分钟。 相信,从 vector<vector<int>> 变换为 set<bitset<180>> 再重构 vector<vector<int>> 所需要的时间也不会长。
由于问题的主体已经解决,故原问题中的删除“空”、“满”记录其实已不重要,这里并没有着重处理。实际上,结果中只可能存在一个“空”或“满”的记录,用“item.reset();data.erase(item);”即可删除。

总结:

解决这个问题的主导思想,在于选择恰当的数据结构,再配合相应的算法。就这个问题来说,关键有两个:一是用 bitset<180> 来表示数据项,二是在 bitset<180> 的基础上,设计一个高效的比较算法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值