【问题标题】:Inserting a Node in a linked list using double pointers使用双指针在链表中插入节点
【发布时间】:2016-06-29 14:49:09
【问题描述】:

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

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;
}

【问题讨论】:

  • 你能链接程序代码吗?
  • @Thomas 不,请不要链接到代码。 OP 应该在他们的问题中提供minimal reproducible example
  • 是的,我添加了一个示例代码,请立即检查

标签: c++ linked-list singly-linked-list double-pointer


【解决方案1】:

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

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

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);
    }
}

或者说句公道话,去掉 cmets:

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 语言中,你经常不得不经常编写链表逻辑,因为泛化链表并不那么容易而且不一定值得,因为那里没有类模板,例如,所以它是方便以更小、更优雅、更不容易出错的方式来编写这些东西。另外,它表明您对指针的理解非常好。

【讨论】:

    【解决方案2】:

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

    没有什么比这很难说的了,但这与从链表中删除节点一样有效。请注意,函数名称 Delete 与 Remove 一样更准确,因为它实际上并没有清理从列表中删除的节点。

    另外,我怎样才能实现类似的方法来插入节点 到一个链表(不跟踪前一个节点)。

    一种方法是向前看。最好在示例中按照删除函数的格式显示。

    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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-02
      • 2019-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-21
      相关资源
      最近更新 更多