结构插入时间极快

时间:2011-12-07 01:21:02

标签: sorting data-structures insertion-sort

我正在寻找一种允许非常快速插入的有序数据结构。这是唯一需要的财产。只会从顶部元素访问和删除数据。

为了更精确,我需要2个结构:

1)第一个结构应允许使用int值进行有序插入。完成插入后,它应报告插入元素的等级

2)第二个结构应该允许插入指定的等级。

要存储的元素数量可能是数千或数万。

[编辑]我必须修改音量假设:即使在任何时刻,有序结构的大小可能在数万的范围内,插入的总数可能在数十每次运行数百万。

O(1)中的插入时间会很好,尽管O(log(log(n)))也是非常可接受的。目前我只有一些有趣的第一结构候选者,但是在log(n)中,或者没有报告插入等级的能力(这是强制性的)。

4 个答案:

答案 0 :(得分:2)

skip-list的形式怎么样,特别是链接文章中的“索引跳过列表”。这应该给O(lg N)插入和查找,并且O(1)访问你的两个用例的第一个节点。

- 编辑 -

当我想到O(1)算法时,我会想到基于基数的方法。这是一个返回等级的O(1)插入。我的想法是将密钥分解为半字节,并保留所有具有该前缀的插入项的计数。不幸的是,常数很高(< = 64 dereferences and additions),并且存储是O(2 x 2 ^ INT_BITS),这很糟糕。这是16位整数的版本,扩展到32位应该很简单。

int *p1;int *p2;int *p3;int *p4;
void **records;
unsigned int min = 0xFFFF;

int init(void)     {
   p1 = (int*)calloc(16,sizeof(int));
   p2 = (int*)calloc(256, sizeof(int));
   p3 = (int*)calloc(4096, sizeof(int));
   p4 = (int*)calloc(65536,sizeof(int));
   records = (void**)calloc(65536,sizeof(void*));
   return 0;
}

//records that we are storing one more item, 
//counts the number of smaller existing items
int Add1ReturnRank(int* p, int offset, int a) {
   int i, sum=0;
   p+=offset;
   for (i=0;i<a;i++)
      sum += p[i];
   p[i]++;
   return sum;
}

int insert(int key, void* data) {
   unsigned int i4 = (unsigned int)key;
   unsigned int i3= (i4>> 4);
   unsigned int i2= (i3>> 4);
   unsigned int i1= (i2>> 4);
   int rank = Add1ReturnRank(p1,0, i1&0xF);
   rank += Add1ReturnRank(p2,i2&0xF0,i2&0xF);
   rank += Add1ReturnRank(p3,i3&0xFF0,i3&0xF);
   rank += Add1ReturnRank(p4,i4&0xFFF0,i4&0xF);
   if (min>key) {min = key;}
   store(&records[i4],data);
   return rank;
}

此结构还支持O(1)GetMin和RemoveMin。 (GetMin是即时的,Remove有一个类似于Insert的常量。)

void* getMin(int* key) {
    return data[*key=min];
}

void* removeMin(int* key)  {
   int next = 0;
   void* data = records[min];
   unsigned int i4 = min;
   unsigned int i3= (i4>> 4);
   unsigned int i2= (i3>> 4);
   unsigned int i1= (i2>> 4);

   p4[i4]--;
   p3[i3]--;
   p2[i2]--;
   p1[i1]--;
   *key = min;
   while (!p1[i1]) {
      if (i1==15) { min = 0xFFFF; return NULL;}
      i2 = (++i1)<<4;
   }
   while (!p2[i2])
      i3 = (++i2)<<4;
   while (!p3[i3])
      i4 = (++i3)<<4;
   while (!p4[i4])
      ++i4;
   min = i4;
   return data;
}

如果您的数据稀疏且分布均匀,则可以删除p4计数器,而是对P3级别执行插入排序。这将使存储成本降低16,代价是在存在许多相似值的情况下更高的最坏情况插入。

改善存储的另一个想法是将这个想法与Extendable Hash之类的东西结合起来。使用整数键作为哈希值,并保持目录中插入节点的计数。对插入上的相关字典条目(如上所述)进行求和仍应为具有大常量的O(1),但存储将减少为O(N)

答案 1 :(得分:1)

订单统计树似乎符合您在O(LogN)时的需求。 Link

  

订单统计树是增强版(参见AugmentedDataStructures)的版本   BinarySearchTree支持附加操作Rank(x),返回排名   x(即,小于或等于x的元素的数量)和FindByRank(k),   它返回树的第k个最小元素。

如果你只有成千上万的元素,那么O(LogN)时间和O(1)时间渐近时间复杂度之间的性能差异并不像你想象的那么重要。例如,考虑100000个元素,logN方法只慢16倍。

  

log(100 000)/ log(2)= 16.6096405

在这种情况下,系数(实现,开销)的差异可能是优化的真正目标。由于继承的复杂性(有时慢了几千倍),花哨的数据结构通常具有更高的开销。它们更可能来自不太精确的实现,因为它们使用较少。

您应该对不同的堆实现进行基准测试(实际测试),以找到具有最佳实际性能的实现。

答案 2 :(得分:1)

你说你需要一个有序的数据结构,对我来说听起来你需要能够产生O(n)时间内所有元素的东西。

但是你说你只会访问顶部(最少?)元素,这表明你真的只需要能够产生最小值的东西,反复 - 打开一个部分排序的东西。

这是什么?

答案 3 :(得分:0)

如果我理解你的问题,我会建议你使用一个字典,其键是排名,值是链表。

使用键可以使用排名并使用链接列表作为值,可以有O(1)插入时间。同样作为删除,你可以有O(1)。您可以使用linkedlist实现堆栈或队列,这就是您想要的。

或者你可以使用双向链表,保证你有O(1)插入和删除。对于排名,您可以在节点中嵌入该信息。