索引集列表的高效数据结构

时间:2013-01-23 09:29:49

标签: algorithm data-structures

我试图通过例子解释:

想象一下编号元素的列表E = [elem0,elem1,elem2,...]。

现在一个索引集可以{42,66,128}引用E中的元素。这个集合中的排序并不重要,所以{42,66,128} == {66,128,42},但是每个元素在任何给定的索引集中最多只有一次(因此它是一个实际的集合)。

我现在想要的是一个节省空间的数据结构,它给我另一个有序列表M,其中包含引用E中元素的索引集.M中的每个索引集只会出现一次(因此M就是这方面的一个集合)但是M必须是可索引的(因此M在这个意义上是List,因此精确的索引并不重要)。如有必要,可以强制索引集包含相同数量的元素。

例如,M可能看起来像:

0: {42, 66, 128}
1: {42, 66, 9999}
2: {1, 66, 9999}

我现在可以执行以下操作:

for(i in M[2]) { element = E[i]; /* do something with E[1],E[66],and E[9999] */ }

你可能会看到它的发展方向:你现在可能有另一个地图M2,它是指向M的有序列表,最终指向E中的元素。

正如你在这个例子中看到的那样,索引集可以相对相似(M [0]和M [1]共享前两个条目,M [1]和M [2]共享最后两个)这使得我认为必须有比使用数组的天真方式更有效的东西。但是,我可能无法提出保证良好“共享”的索引条目的良好全局排序。

我可以想到任何事情,从表示M作为树(其中M的索引来自深度优先搜索顺序或其他东西)到联合查找结构的哈希映射(不知道它将如何工作:)

对于任何这样的教科书数据结构的指针都非常受欢迎(数据库世界中有什么吗?)但我也很感激你提出一个“自制的”解决方案或只是随意的想法。

空间效率对我来说很重要,因为E可能包含数千甚至数百万个元素,(某些)索引集可能很大,至少某些索引集之间的相似性应该很大,并且可能存在多层映射。 / p>

非常感谢!

4 个答案:

答案 0 :(得分:1)

击败指数将非常困难。您可以使用正确的数据类型来节省一些空间(例如,在gnu C中,,如果E中的元素少于64k, int ,如果< 4G ...)。

此外,

由于您说E中的顺序并不重要,您可以对E进行排序,使其最大化连续元素以尽可能地匹配Ms。

例如,

E: { 1,2,3,4,5,6,7,8 }

0: {1,3,5,7}
1: {1,3,5,8}
2: {3,5,7,8}

重新安排E

E: { 1,3,5,7,8,2,4,6 }

并使用E索引而不是值,您可以根据E的子集定义Ms,给出索引

0: {0-3}     // E[0]: 1, E[1]: 3, E[2]: 5, E[3]: 7 etc...
1: {0-2,4}
2: {1-3,4}

这样

  • 您使用索引而不是原始数字(索引通常较小,没有负数..)
  • Ms由子集组成,0-3表示0,1,2,3,

困难的部分是使算法重新排列E,以便最大化子集大小 - 最小化Ms大小。

E重新安排算法建议

  • 排序所有Ms
  • 处理所有Ms:

algo构建一个地图,它为一个元素'x'提供了邻居'y'的列表,以及 points ,'y'就在'x'之后的次数

Map map (x,y) -> z
for m in Ms
  for e,f in m // e and f are consecutive elements
    if ( ! map(e,f)) map(e,f) = 1
    else map(e,f)++
  rof
rof

重新安排E

ER = {}                 // E rearranged
Map mas = sort_map(map) // mas(x) -> list(y) where 'y' are sorted desc based on 'z' 
e = get_min_elem(mas)   // init with lowest element (regardless its 'z' scores)


while (mas has elements) 
  ER += e        // add element e to ER
  f = mas(e)[0]  // get most likely neighbor of e (in f), ie first in the list
  if (empty(mas(e))
    e = get_min_elem(mas) // Get next lowest remaining value
  else
    delete mas(e)[0]  // set next e neighbour in line
    e = f
  fi
elihw

算法(地图)应为O(n*m)空格,E中有n个元素,所有Ms中有m个元素。

答案 1 :(得分:1)

您可以合并M中的所有数字并删除重复项,并将其命名为UniqueM。

所有M [X]集合都转换为位掩码。例如,int值可以存储32个数字(为了支持无限计数,你应该存储整数数组,如果数组大小为10,我们可以存储320个不同的元素)。 long类型可以存储64位。

E: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

M[0]: {6, 8, 1}
M[1]: {2, 8, 1}
M[2]: {6, 8, 5}

将转换为:

UniqueM: {6, 8, 1, 2, 5}
M[0]: 11100 {this is 7}
M[1]: 01110 {this is 14}
M[2]: 11001 {this is 19}

注意: 你也可以结合我和ring0方法,而不是重新排列E make new UniqueM并在其中使用区间。

答案 2 :(得分:1)

可以使用位数组。它们是元素a[i]的数组,1如果i已设置,0如果i未设置。因此,即使它包含少量成员也不包含任何成员,每个集合都会占用size(E)位。不是那么节省空间,但是如果用一些压缩算法压缩这个数组,它的大小会小得多(可能达到最终的熵限制)。因此,您可以尝试动态马尔可夫编码器或RLE或组合霍夫曼,并为您选择最有效的。然后,迭代过程可以包括动态解压缩,然后是1位的线性扫描。对于looong 0运行,您可以修改解压缩算法以检测此类情况(RLE是最简单的情况)。

如果您发现集合具有较小的差异,则可以存储集AA xor B而不是AB,以节省公共部分的空间。在这种情况下,要迭代B,您必须将AA xor B以及xor解包。

答案 3 :(得分:0)

另一个有用的解决方案:

E: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

M[0]: {1, 2, 3, 4, 5, 10, 14, 15}
M[1]: {1, 2, 3, 4, 5, 11, 14, 15}
M[2]: {1, 2, 3, 4, 5, 12, 13}

缓存常用项目

Cache[1] = {1, 2, 3, 4, 5}
Cache[2] = {14, 15}
Cache[3] = {-2, 7, 8, 9} //Not used just example.

M[0]: {-1, 10, -2}
M[1]: {-1, 11, -2}
M[2]: {-1, 12, 13}

将指向缓存列表的链接标记为负数。