您的位置:首页 > 其它

an efficient representation for sparse set

2013-01-17 09:02 267 查看
根据同名论文翻译而来。这里说的set,是数学意义上的set,就是集合。一般实现的时候,有两种方法,一是树,二是hash。这两种方法比较通用,但是不够高效。对于特定的场合,比如知道set里元素的取值范围是[0,N],N不会太大,这时候就可以直接用一个bool数组标记这个元素在不在set里。但是如果N比较大的话,会有两个问题,一是初始化时间比较长,二是遍历的时间比较长,都是O(N)的。

假设集合只有n个元素n<<N,有没有什么办法把初始化与遍历的复杂度都降为O(n)呢?

以前设计hash表的时候,用过一个技巧是,给每个元素一个版本号。但是第一次使用的时候,还是需要给整个数组赋初值,复杂度为O(N),而且遍历的复杂度也还是O(N).

为了实现O(n)遍历,直观的想法是,另外分配一个vector,每个插进去的元素,同时加进bool数组和vector里,查询的时候用bool数组,遍历的时候用vector。

怎么支持删除操作呢?bool 数组的删除容易,vector就麻烦了,需要遍历。需要给vector建一个索引,索引本身是一个map,可以用树,也可以用hash实现。这么一想,问题就递归了。其实有简单的方案,不再是简单的bool 数组,而是用一个int数组,数组里元素的值指向它在vector里的下标。根据下标从vector删除数据之后,后面的数据不能往前挪,挪的话,int数组里的索引就全乱了,只能标记一下已删除。遍历的时候特殊处理一下。这样经过反复增删之后,vector里就会有大量的删除结点,性能和内存都不理想。如果让后面新增的数据直接塞到已删除的结点,可以避免内存的无限增长。但是需要维护另一个链表来存储已经删除的结点。而且无法解决遍历性能的下降。

所以vector里的数据,也应该有一个指向int数组的反向索引,这样就可以挪动vector数组,并且更新int数组里的索引。平常的做法是删了一个元素后,后面的元素逐个往前挪,这复杂度是O(n)的。更好的作法是,直接把最后一个元素填到被删的元素那里,这样只需要一次操作。

这个结构还有一个优点,可以很简单的支持清空操作。vector如果不回收内存的话,可以在O(1)时间清空,直接把size清0。然后再利用vector去清空int 数组。int 数组指向vector里一个下标,如果这个下标超过了vector的size,那这个肯定是老数据;如果没超过,但vector里对应位置的数据肯定是已经更新过的,如果vector里的数据,并没有反向指回去,那int数组里肯定也是老数据。

真正代码实现的时候,比较简单。
http://hi.baidu.com/rodimus/item/38101eb366c63a95184697a4
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐