我正在寻找一种允许非常快速插入的有序数据结构。这是唯一需要的财产。只会从顶部元素访问和删除数据。
为了更精确,我需要2个结构:
1)第一个结构应允许使用int值进行有序插入。完成插入后,它应报告插入元素的等级。
2)第二个结构应该允许插入指定的等级。
要存储的元素数量可能是数千或数万。
[编辑]我必须修改音量假设:即使在任何时刻,有序结构的大小可能在数万的范围内,插入的总数可能在数十每次运行数百万。
O(1)中的插入时间会很好,尽管O(log(log(n)))也是非常可接受的。目前我只有一些有趣的第一结构候选者,但是在log(n)中,或者没有报告插入等级的能力(这是强制性的)。
答案 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)插入和删除。对于排名,您可以在节点中嵌入该信息。