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> 的基础上,设计一个高效的比较算法。相关文章推荐
- 数据结构—约瑟夫问题
- 数据结构与算法分析java——散列
- 数据结构--单向循环链表、双向循环链表
- C++ 数据结构 广义表
- 【从下而上学习Redis】数据结构篇(一):跳跃表(skiplist)
- C语言的各种数据结构所占的字节数
- 数据结构--单链表
- 数据结构-栈的实现之迷宫解密
- 数据结构与算法应用情景总结
- oj数据结构题谜之输出格式
- 数据结构复习总结
- 数据结构:多叉树的建立
- 数据结构概论
- 第三章:数据决定数据结构
- 线段树--数据结构专题学习
- 数据结构的二叉链表中序遍历
- 【裸线段树】Uestc-数据结构专题训练【A】
- 【线段树+离散化】Uestc-数据结构专题训练【B】
- 数据结构报告
- Java基础数据结构和算法