使用双指针在链表中插入一个节点

Inserting a Node in a linked list using double pointers

本文关键字:节点 一个 插入 指针 链表      更新时间:2023-10-16

我最近看到了一个使用双指针从单个链表中删除节点的实现。除了使代码更美观之外,这种实现在效率方面有什么好处吗?另外,我如何实现一个类似的方法来插入一个节点到一个链表(不跟踪以前的节点)。我真的很好奇是否有更好的算法来实现这个

Node* Delete(Node *head, int value)
{
    Node **pp = &head; /* pointer to a pointer */
    Node *entry = head;
    while (entry ) 
    {
        if (entry->value == value)
        {
            *pp = entry->next;
        }
        pp = &entry->next;
        entry = entry->next;
    }
    return head;
}

对于只存储头而不存储尾的列表的后面插入(这意味着可以接受线性时间插入的小列表),可以通过间接引入额外的指针来消除特殊情况:

简单版本(指针指向节点的指针)

void List::push_back(int value)
{
    // Point to the node link (pointer to pointer to node), 
    // not to the node.
    Node** link = &head;
    // While the link is not null, point to the next link.
    while (*link)
        link = &(*link)->next;
    // Set the link to the new node.
    *link = new Node(value, nullptr);
}

…你可以简化为:

void List::push_back(int value)
{
    Node** link = &head;
    for (; *link; link = &(*link)->next) {}
    *link = new Node(value, nullptr);
}

与之相对的是:

复杂版本(指针指向节点)

void List::push_back(int value)
{
    if (head)
    {
        // If the list is not empty, walk to the back and
        // insert there.
        Node* node = head;
        while (node->next)
             node = node->next;
        node->next = new Node(value, nullptr);
    }
    else
    {
        // If the list is empty, set the head to the new node.
        head = new Node(value, nullptr);
    }
}

或者为了公平起见,删除注释:

void List::push_back(int value)
{
    if (head)
    {
        Node* node = head;
        for (; node->next; node = node->next) {}
        node->next = new Node(value, nullptr);
    }
    else
        head = new Node(value, nullptr);
}

简单版本无特殊情况

第一个版本不需要特殊的空列表的主要原因是,如果我们想象head是空的:

Node** link = &head; // pointer to pointer to null
for (; *link; link = &(*link)->next) {}
*link = new Node(value, nullptr);

for循环条件立即为假,然后我们将新节点分配给head。当使用指针指向指针时,不必在循环外单独检查这种情况。

插入排序

如果你想做一个插入排序,而不是简单地插入到后面,然后:

void List::insert_sorted(int value)
{
    Node** link = &head;
    for (; *link && (*link)->value < value; link = &(*link)->next) {}
    // New node will be inserted to the back of the list
    // or before the first node whose value >= 'value'.
    *link = new Node(value, *link);
}

至于性能,不确定消除额外的分支会有多大不同,但它肯定会使代码更紧凑,并降低其圈复杂度。Linus认为这种风格"很有品味"的原因是,在C语言中,你经常需要编写链表逻辑,因为它不那么容易,而且泛化链表并不一定值得,因为我们没有类模板,所以用一种更小、更优雅、更少出错的方式来编写这些东西是很方便的。此外,它还表明您对指针的理解相当好。

这个实现除了使代码更漂亮之外在效率方面有任何好处。

没有什么可以与之比较的,很难说,但这和你从链表中删除一个节点一样有效。请注意,函数名Delete应该更准确地称为Remove,因为它实际上并没有清除它从列表中删除的节点。

我也可以实现插入节点的类似方法到一个链表(不跟踪前一个节点)。

一种方法是向前看。最好在Delete函数格式的示例中展示。
void insert(Node *head, int value)
{
    Node *entry = head;
    while (entry)
    {
        if (entry->next == NULL)
        {
            entry->next = new Node(NULL, value);
            return;
        }
        else if (value < entry->next->value)
        {
            entry->next = new Node(entry->next, value);
            return;
        }
        entry = entry->next;
    }
}
相关文章: