您的位置:首页 > 理论基础 > 数据结构算法

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

2016-04-23 15:03 525 查看

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

原文:http://blog.csdn.net/rushman/article/details/2316642
问题的来源: 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 ]
= {0,3,179}
;

a[ 4 ]
= {1,2,3,4,5,6,7,8,9,10...,179}
; //
 “满”,表示从0到179间的数均在该vector中
// ....

a[ 310000
] =
{/*...*/};

每个vector <int>中是没有重复的数据的。即不可能出现类似a
={1,3,25,25,98}中有2个25的情况。

先要对上述31万个vector <int>进行压缩,压缩原则为

空vector <int>删除,如a[1]
“满”vector <int>删除,如a[4]
相同vector <int>删除其中一个,如a[0]和a[3]相同,则删除其中任意一个
互补vector <int>删除其中一个,互补的意思是两vector <int>不相交且其并集为“满”vector <int>

问题分析:

对于问题所描述的数据,不难看出,不管是 a 的各个项还是 a
的各个项,都具有唯一、无序(有顺序但不重要)的典型的集合的特征。或者说,问题的目的就是要把原来的数据转换为一个集合的集合。

这里需要注意的是,根据原则 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> 的基础上,设计一个高效的比较算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: