已展开的跳过列表的实际用法

时间:2015-02-16 10:49:28

标签: data-structures linked-list skip-lists

为什么Google / Wikipedia中没有关于展开的跳过列表的任何信息?例如展开的链表和跳过列表之间的组合。

2 个答案:

答案 0 :(得分:1)

可能是因为它通常不会给你带来很大的性能提升(如果有的话),并且在某种程度上可以正确编码。

首先,展开的链表通常使用非常小的节点大小。正如Wikipedia article所说:"只要足够大,以便节点填充单个缓存行或其中的一小部分。"在现代英特尔处理器上,缓存行为64字节。跳过列表节点平均 ,每个节点有两个指针,这意味着前向指针平均每个节点16个字节。无论节点的数据是什么:标量值为4或8字节,或参考为8字节(我假设64位机器在这里)。

因此,"元素的数字为24字节,总数。"除了元素不是固定大小。它们具有不同数量的前向指针。因此,您需要通过为每个元素的最大前向指针数分配一个数组来使每个元素成为固定大小(对于具有32个级别的跳过列表,需要256个字节),或者使用动态分配的数组。是正确的尺寸。所以你的元素本质上变成了:

struct UnrolledSkipListElement
{
    void* data; // 64-bit pointer to data item
    UnrolledSkipListElement* forward_pointers; // dynamically allocated
}

这会将元素大小减少到只有16个字节。但是,您失去了很多与展开相关的缓存友好行为。要找出接下来的位置,您必须取消引用forward_pointers阵列,这将导致缓存未命中,从而消除了通过展开而节省的成本。另外,动态分配的指针数组是免费的:分配该内存需要一些(小的)开销。

如果你能找到解决问题的方法,你仍然不会获得太多收益。展开链接列表的一个重要原因是,当您进行搜索时,必须访问每个节点(直到您找到的节点)。因此,任何时候你可以节省每个链接遍历加起来非常大的节省。但是使用跳过列表可以进行大跳跃。例如,在完美组织的跳过列表中,您可以跳过第一次跳转的一半节点(如果您要查找的节点位于列表的后半部分)。如果展开的跳过列表中的节点只包含四个元素,那么您获得的唯一节省将是0,1和2级。在更高级别,您将跳过超过三个节点,因此您将招致缓存未命中。

因此,跳过列表没有展开,因为它实际上有些参与,如果有的话,它不会给你带来很大的性能提升。它可能会导致列表变慢。

答案 1 :(得分:0)

链接列表复杂度为O(N)

跳过列表复杂度为O(Log N)

展开的链接列表复杂性可以计算如下:

O(N /(M / 2)+ Log M)= O(2N / M + Log M)

其中M是单个节点中的元素数。

因为Log M不重要,

展开的链接列表复杂度为O(N / M)

如果我们假设将Skip list与Unrolled链表结合起来,那么新的复杂性将是

O(记录N +"来自展开的链接列表的内容,例如N1 / M")

这意味着"新"复杂性不会像第一个人想的那样好。新的复杂性可能比原始的O(Log N)更糟糕。实施也将更加复杂。因此,收益是值得怀疑的,而且相当可疑。

此外,由于单个节点将拥有大量数据,但只有单个"转发"数组,"树"也不会如此平衡,这将破坏等式的O(Log N)部分。