【问题标题】:Why does the doubly linked list in sys/queue.h maintain the address of previous next element?为什么 sys/queue.h 中的双向链表会维护前一个下一个元素的地址?
【发布时间】:2013-05-02 16:17:53
【问题描述】:

我正在从 FreeBSD 学习 sys/queue.h,我有一个问题:

sys/queue.h中,LIST_ENTRY定义如下:

#define LIST_ENTRY(type)                        \
struct {                                \
    struct type *le_next;   /* next element */          \
    struct type **le_prev;  /* address of previous next element */  \
}

为什么它维护前一个元素的地址struct type **le_prev)而不是像struct type *le_prev那样简单的previous elment

【问题讨论】:

    标签: c struct queue doubly-linked-list bsd


    【解决方案1】:

    如果您从头开始阅读 queue.h 文件,您可以 有以下评论:

     * A list is headed by a single forward pointer (or an array of forward
     * pointers for a hash table header). The elements are doubly linked
     * so that an arbitrary element can be removed without a need to
     * traverse the list. New elements can be added to the list before
     * or after an existing element or at the head of the list. A list
     * may only be traversed in the forward direction.
    

    so 列表,它提供 O(1) 的插入和删除, 但只能向前遍历。为此,您只需要 引用先前的 next 指针,这正是 实施。

    【讨论】:

    • 你的意思是说这个实现是为了防止后向遍历以及获得O(1)的插入和删除?
    • @YanzheChen 使用单指针时(线性)列表操作的复杂性不会改变,并且双指针不会阻止向后遍历。我认为,重要的部分是“或一组前向指针”;当有这样一个(散列)表时,当存储前一个指针的地址时,删除会更便宜。
    • @ensc 我读了这个post,明白如果不是尾节点,单链表中的删除可以在O(1)中实现。但是为什么双指针 not 会阻止向后遍历呢?如何执行反向遍历?我也不明白你提到的重要部分。你能详细解释一下吗?
    • @YanzheChen 向后遍历将是n = container_of(*n->le_prev, type, le_next)重要部分适用于列表的第一个元素(根)是特殊的情况;一个典型的例子是哈希树的根,它由指向列表的链接数组组成。在这里,您不能取消引用锚(您不知道数组中的索引),因此必须使用双指针。
    • 我认为这个答案更好地解释了它:stackoverflow.com/questions/3058592/…
    【解决方案2】:

    让我试着解释一下。 实际上,**le_prev* 提供了将 sys/queue.h 定义的列表到 insert_before 的能力,而 forward-list 不能。与insert_before相比,insert_after在forward-list或list中都可以很好地实现。所以列表更实用。

    insert_before(entry* list_elem, entry* elem, type val)
    {
        elem->next = list_elem;
        *(list->prev) = elem;
        elem->prev = *(list->prev);
        list_elem->prev = elem->next;
    }
    insert_after(entry* list_elem, entry* elem, type val)
    {
        if( ((elem)->next= (list_elem)->next) != NULL ) {
            (elem_list)->next->prev = &(elem)->next;
        }
        (list_elem)->next =  elem;
        elem->prev =  &(list_elem)->next;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多