Sorting 具有极快插入时间的结构
我正在寻找一个有序的数据结构,允许非常快的插入。这是唯一需要的财产。只能从顶部元素访问和删除数据 更精确地说,我需要两个结构: 1) 第一个结构应该允许使用int值进行有序插入。完成插入后,应报告插入元素的排名 2) 第二个结构应允许在指定的列组中插入 要存储的元素数可能是数千或数万 [编辑]我必须修正体积假设:尽管在任何时候,有序结构的大小可能在数万范围内,但每次运行的插入总数可能在数千万范围内Sorting 具有极快插入时间的结构,sorting,data-structures,insertion-sort,Sorting,Data Structures,Insertion Sort,我正在寻找一个有序的数据结构,允许非常快的插入。这是唯一需要的财产。只能从顶部元素访问和删除数据 更精确地说,我需要两个结构: 1) 第一个结构应该允许使用int值进行有序插入。完成插入后,应报告插入元素的排名 2) 第二个结构应允许在指定的列组中插入 要存储的元素数可能是数千或数万 [编辑]我必须修正体积假设:尽管在任何时候,有序结构的大小可能在数万范围内,但每次运行的插入总数可能在数千万范围内 在O(1)中插入时间会很好,尽管O(log(log(n))也是可以接受的。目前,我只为第一个结构找
在O(1)中插入时间会很好,尽管O(log(log(n))也是可以接受的。目前,我只为第一个结构找到了一些有趣的候选者,但要么在log(n)中,要么没有报告插入排名的功能(这是必需的)。顺序统计树在O(LogN)时似乎适合您的需要 订单统计信息树是一个扩展(请参见增强数据结构)版本的
BinarySearchTree,支持额外的操作秩(x),它返回秩 x(即键小于或等于x的元素数)和FindByRank(k), 返回树的第k个最小元素 如果您只有数万个元素,那么O(LogN)时间和O(1)时间渐近时间复杂度之间的性能差异就没有您想象的那么显著。例如,考虑100000个元素,Logn方法只慢16倍。 对数(100000)/对数(2)=16.6096405 在这种情况下,系数差异(实现、开销)可能是优化的真正目标。由于继承的复杂性,奇特的数据结构通常具有更高的开销(有时慢数千倍)。它们更可能来自不太完善的实现,因为它们使用较少
您应该对不同的堆实现进行基准测试(实际测试),以找到具有最佳实际性能的实现 你说你需要一个有序的数据结构,对我来说,这听起来像是你需要一个能够产生O(n)时间中包含的所有元素的东西 但是你说你将只访问顶部(最少?)元素,这意味着你真的只需要一些可以产生最小值的东西,重复地——打开一扇通向偏序的门
是哪一个?如果我正确理解了你的问题,我建议你使用一本字典,它的键是等级,值是链表 使用关键点可以具有列组,使用链表作为值,可以具有O(1)个插入时间。同样作为删除,您可以有O(1)。您可以使用linkedlist实现堆栈或队列,这正是您所需要的 或者,您可以使用一个双链接列表,其中保证有O(1)个插入和删除。对于排名,您可以将该信息嵌入到节点中。关于一种形式的,特别是链接文章中的“索引skiplist”如何。这将为您的两个用例提供O(lgn)插入和查找,以及O(1)访问第一个节点的权限 --编辑--
当我想到O(1)算法时,我想到的是基于基数的方法。这是一个返回秩的O(1)插入。这样做的目的是将密钥分解为多个半字节,并对所有插入的具有该前缀的项进行计数。不幸的是,这个常数很高(我一直在研究堆数据结构;但是,尽管它们提供了一些非常好的插入速度和快速的顶级元素检索,但它们似乎没有提供插入元素的排名,也不允许在指定的排名插入。您查看过顺序统计树吗?。这似乎是为您的需要而设计的。我有一笔费用我认为,如果您需要了解数据结构的“中间”部分,那么您不会比日志(n)时间和树做得更好。我不建议使用堆,因为每次插入节点时您都必须进行堆化,最终会得到O(nlogn)@desmond:这很有趣,我会研究一下。谢谢当你说有序时,你是说保持插入的顺序?还是排序?你能解释一下为什么数组(可能是预先分配的,或者根据需要动态调整大小为更大的)吗不符合您的条件?或链表?我怀疑您还有其他未列出的要求。@DarthVader:是,“已排序”。每次插入后都应对结构进行排序。@Phrogz:事实上,我目前的幼稚实现确实使用链表和环表。它们可以工作。但它们不尊重O(1)(或足够接近)属性。它们太慢了。想想当尝试将一个元素插入10K排序列表/表的中间时会发生什么…@Cyan啊,插入到中间。当您说“有序数据结构”但没有指定顺序时,我想您可能是指“尊重插入顺序”。您的意思是您想要一个执行排序插入的数据结构(我假设是针对某些排序标准)。好的观点。真正的问题是,我需要在插入元素时检索排名。因此,我猜必须对结构进行排序/排序才能提供排名。事实上,我正在考虑这种可能性。可以是一个很好的候选者。它看起来尽可能好。我也在考虑类似的想法,使用Fenwick树。我担心大内存需求会降低性能,因为这也意味着频繁的非缓存内存访问。但至少值得一试。这看起来是一个有趣的命题,但我还不太明白。键怎么会变为秩?秩应该在每次插入后一直变化
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;
}
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;
}