Language agnostic 在什么情况下链表有用?

Language agnostic 在什么情况下链表有用?,language-agnostic,data-structures,linked-list,Language Agnostic,Data Structures,Linked List,大多数时候,我看到人们试图使用链表,在我看来,这似乎是一个糟糕(或非常糟糕)的选择。也许有必要探讨在何种情况下链表是或不是数据结构的良好选择 理想情况下,答案将阐明选择数据结构时使用的标准,以及在特定情况下哪些数据结构可能工作得最好 编辑:我必须说,我对答案的数量和质量印象深刻。我只能接受一个,但还有两三个我不得不说,如果没有更好一点的东西,我是值得接受的。只有一对夫妇(特别是我最终接受的那一对)指出,在某些情况下,链表提供了真正的优势。我认为史蒂夫·杰索普不仅给出了一个答案,而且给出了三个不同

大多数时候,我看到人们试图使用链表,在我看来,这似乎是一个糟糕(或非常糟糕)的选择。也许有必要探讨在何种情况下链表是或不是数据结构的良好选择

理想情况下,答案将阐明选择数据结构时使用的标准,以及在特定情况下哪些数据结构可能工作得最好

编辑:我必须说,我对答案的数量和质量印象深刻。我只能接受一个,但还有两三个我不得不说,如果没有更好一点的东西,我是值得接受的。只有一对夫妇(特别是我最终接受的那一对)指出,在某些情况下,链表提供了真正的优势。我认为史蒂夫·杰索普不仅给出了一个答案,而且给出了三个不同的答案,我觉得这三个答案都给人留下了深刻的印象,因此他应该得到某种荣誉称号。当然,尽管它只是作为评论而不是答案发布,但我认为Neil的博客文章也很值得一读——不仅信息丰富,而且非常有趣。

我过去在C/C++应用程序中使用过链表(甚至是双链表)。这是在.NET甚至stl之前


我现在可能不会在.NET语言中使用链表,因为您需要的所有遍历代码都是通过Linq扩展方法提供的。

当您需要高速推送、弹出和旋转时,它们非常有用,而且不介意O(n)索引。

链表非常灵活:只需修改一个指针,您可以进行大规模更改,在数组列表中,相同的操作将非常低效。

它们对于并发数据结构非常有用。 (下面是一个非并发的实际使用示例——如果没有提到FORTRAN,就不会有这个示例了。)

例如,.NET4.0RC中的
ConcurrentDictionary
使用链表将散列到同一存储桶中的项目链接起来

ConcurrentStack
的底层数据结构也是一个链表

<代码>并发堆栈是作为基础的数据结构之一((本地)队列基本上是堆栈实现的。(另一个主要支撑结构是

ConcurrentQueue

新线程池反过来为新线程的工作调度提供了基础

因此,它们肯定是有用的——链表目前是至少一项伟大的新技术的主要支持结构之一

(在这些情况下,单链表是一个令人信服的选择,但不是等待自由的选择,因为主操作只需一次(+重试)。 在现代GC-d环境中(如Java和.NET),可以很容易地避免这种情况。 只需将您添加的项包装到新创建的节点中,而不要重用这些节点——让GC完成它的工作。 关于ABA问题的页面还提供了一个无锁堆栈的实现,该堆栈实际上在.Net(&Java)中工作,并有一个(GC-ed)节点保存这些项。)

编辑: @尼尔: 事实上,您提到的FORTRAN提醒我,在.NET中使用和滥用最多的数据结构中也可以找到相同类型的链表: 普通.NET通用
词典

不是一个,而是许多链表存储在一个数组中

  • 它避免了在插入/删除时执行许多小的(取消)分配
  • 哈希表的初始加载速度非常快,因为数组是按顺序填充的(CPU缓存非常好)
  • 更不用说链式散列表在内存方面是昂贵的——而且这个“技巧”将x64上的“指针大小”减少了一半
本质上,许多链表存储在一个数组中。(使用的每个铲斗一个。) 可重用节点的自由列表在它们之间“交织”(如果存在删除)。
在开始/重新刷新时分配一个数组,并在其中保留链的节点。在deletes之后还有一个自由指针(数组的索引)所以-信不信由你-FORTRAN技术仍然存在。(…在最常用的.NET数据结构之一中;-)。

当您需要对任意(编译时未知)长度的列表进行大量插入和删除,但不需要太多搜索时,链表非常有用

拆分和连接(双向链接)列表非常有效

您还可以组合链表-例如,树结构可以实现为“垂直”链表(父/子关系),将水平链表(同级)连接在一起

出于这些目的使用基于阵列的列表有严重的局限性:

  • 添加新项意味着必须重新分配阵列(或者必须分配比需要更多的空间,以允许未来的增长并减少重新分配的数量)
  • 删除项目会浪费空间或需要重新分配
  • 在除末端以外的任何位置插入项都涉及(可能重新分配和)将大量数据复制到一个位置

对于单元分配器或对象池中的空闲列表,单链接列表是一个不错的选择:

  • 您只需要一个堆栈,因此单链接列表就足够了
  • 所有内容都已划分为节点。如果单元格足够大,可以包含指针,则入侵列表节点没有分配开销
  • 向量或deque会对每个块施加一个指针的开销。这一点非常重要,因为当您第一次创建堆时,所有单元都是免费的,因此这是一项前期成本。在最坏的情况下,它会使每个单元的内存需求翻倍

  • 双链表是定义hashmap顺序的好选择,hashmap还定义元素的顺序(Java中的LinkedHashMap),尤其是按上次访问排序时:

  • 更多备忘录
    struct FaceVertex
    {
        // Points to next vertex in polygon or -1
        // if we're at the end of the polygon.
        int next;
        ...
    };
    
    struct Polygon
    {
         // Points to first vertex in polygon.
        int first_vertex;
        ...
    };
    
    struct Mesh
    {
        // Stores all the face vertices for all polygons.
        std::vector<FaceVertex> fvs;
    
        // Stores all the polygons.
        std::vector<Polygon> polys;
    };