链表添加到尾部,混乱

时间:2009-12-21 14:00:41

标签: c linked-list

Visual Studio 2008 C

我对这个链接列表无法理解的是在if语句的else部分添加尾部。

当头部和尾部被分配时,node_temp的尾部和头部的内存地址都指向相同的内存位置。

但是,在else部分中,头部实际上仍然指向尾部。有些东西我无法解释,也不了解其他部分?

我希望有人可以更好地为我解释。

static struct convert_temp
{
    size_t cel;
    size_t fah;
    struct convert_temp *next;
} *head = NULL, *tail = NULL;

/** Add the new converted temperatures on the list */
void add(size_t cel, size_t fah)
{
    struct convert_temp *node_temp = NULL; /* contain temp data */

    node_temp = malloc(sizeof(*node_temp));

    if(node_temp == NULL)
    {
        fprintf(stderr, "Cannot allocate memory [ %s ] : [ %d ]\n",
            __FUNCTION__, __LINE__);
        exit(0);
    }

    /* Assign data */
    node_temp->cel = cel;
    node_temp->fah = fah;
    node_temp->next = NULL;

    if(head == NULL)
    {
        /* The list is at the beginning */
        head = node_temp;   /* Head is the first node = same node */
        tail = node_temp;   /* Tail is also the last node = same node */
    }
    else
    {
        /* Append to the tail */
        tail->next = node_temp;
        /* Point the tail at the end */
        tail = node_temp; 
    }
}

4 个答案:

答案 0 :(得分:27)

第一次将一个元素(我们称之为A)添加到列表中时,head为空,您将浏览if部分。这意味着在添加第一个元素时,head和tail都设置为指向A

现在让我们添加另一个元素B。这一次,head不为空,因此它会通过else部分,将tail设置为B,但head指向A }。

这是预期的,您现在head指向AA指向BB指向空(null)和{ {1}}指向tail

让我们一步一步来。

B

你可以在每个阶段(除了初始阶段)看到当前尾部被设置为指向新节点(已经指向其下一个节点为NULL),然后尾部指针被更新为指向 new 最后一个节点。

事实上,让我们在 more 细节中逐行添加C(逐行),这样你就可以看到每行代码的作用(我已重命名为Initial state: head -+-> null | tail -+ Insert item A: head -+-> A ---> null | tail -+ Insert item B: head ---> A -+-> B ---> null | tail --------+ Insert item C: head ---> A ---> B -+-> C ---> null | tail ---------------+ node_temp只是为了帮助格式化):

node

然后最终Starting state: head ---> A -+-> B ---> null | tail --------+ node = malloc(sizeof(*node)); node ---> C ----------> ? (allocate node C) head ---> A -+-> B ---> null | tail --------+ node->next = NULL; node ---> C --------+ (ignore payload cel/fah | for now since it's not head ---> A -+-> B -+-> null relevant to the list | structure) tail --------+ tail->next = node; node ---------------+ (first in else clause) | head ---> A -+-> B -+-> C ---> null | tail --------+ tail = node; node ---------------+ (second in else clause) | head ---> A ---> B -+-> C ---> null | tail ---------------+ 消失,因为它是一个局部变量并且你有最终状态:

node

在单链表中维护 head ---> A ---> B -+-> C ---> NULL | tail ---------------+ 指针的优点是,当您尝试将项目添加到最后时,必须单步执行整个列表以找到结束。

遍历整个列表会使最后插入tail次操作(所用时间取决于列表中的项目数)。 O(n)指针的使用使得tail时间操作(与列表大小无关的时间相同)。

顺便说一下,双向链表对O(1)指针有额外的用处 - 它使用tail和快速开始从列表末尾到开始的遍历。 tail指针代替prevhead指针。

答案 1 :(得分:4)

else-part只更新列表的tail,因为当你附加到链接列表时头部不会改变。

保持指向缓冲区尾部元素的指针是一种优化,因此您不必在每个附加项的头部逐步完成整个列表。

答案 2 :(得分:1)

头部仍然指向尾巴。 Head指向尾部。当列表只包含一个元素时,它既是头部也是尾部。附加新节点后,尾指针已更新。头指针仍然指向第一个节点,这是正确的。

答案 3 :(得分:1)

在第一次调用add时,head和tail将指向新创建的内存块。所有后续的add调用都将通过else部分进行调整,这只会改变尾部指针,基本上修改旧的tail->然后指向新的内存块,然后更新tai​​l也指向这个新的内存块。

这是一种有效的追加方式。如果只使用了head,那么每次添加一个新的node_temp时,你必须从头开始遍历所有下一个指针,直到你到达之前添加的node_temp(其下一个指针为NULL),然后添加新节点。这将是O(n)算法,而不是上面的O(1)。