简体   繁体   English

删除单链表中的节点

[英]Remove node in a singly linked list

Suppose you have a pointer p to a node in a singly linked list that is not on the last node in the list.假设你有一个指针 p 指向一个单向链表中的一个节点,该节点不在链表的最后一个节点上。 You have no other pointer to the list except the following links in each node.除了每个节点中的以下链接之外,您没有其他指向列表的指针。

Describe an O(1) algorithm that logically removes the value stored in the node pointed to by p (suggestion: use the next node).描述一个 O(1) 算法,该算法在逻辑上删除存储在 p 指向的节点中的值(建议:使用下一个节点)。

The idea to do it is to transfer information from the next node to the current node pointed to by p and the next node is removed from the list.这样做的想法是将信息从下一个节点传输到 p 指向的当前节点,然后从列表中删除下一个节点。 My question is why don't we remove the node pointed by our pointer instead of removing the next node.我的问题是为什么我们不删除指针指向的节点而不是删除下一个节点。 I am a bit confused.我有点困惑。

You have a pointer to the node, but no way to change the previous node's reference to it;您有一个指向该节点的指针,但无法更改前一个节点对它的引用; therefore you must copy and remove the next node.因此,您必须复制并删除下一个节点。

Your pointer p only references the current node.您的指针p仅引用当前节点。 Since you can't modify prevous->next to point at the node with value 3, you must copy next node into the current one, both value and next pointer.由于您不能修改prevous->next以指向值为 3 的节点,因此您必须将下一个节点复制到当前节点,包括值和下一个指针。

在此处输入图像描述

My question is why don't we remove the node pointed by our pointer instead of removing the next node.我的问题是为什么我们不删除指针指向的节点而不是删除下一个节点。

You can't remove the current node.您不能删除当前节点。

The question states:问题指出:

You have no other pointer to the list except the following links in each node.除了每个节点中的以下链接之外,您没有其他指向列表的指针。

This means that there are nodes prior to yours and, in particular, there is an immediately prior node that points to yours.这意味着在您之前有节点,特别是,有一个直接在前的节点指向您的节点。 You cannot delete your node because you have no way of changing the immediately prior node.您无法删除您的节点,因为您无法更改前一个节点。

You can't remove the next node.您不能删除下一个节点。

The question states that you need to remove:该问题指出您需要删除:

the value stored in the node pointed to by p.存储在 p 指向的节点中的值。

That's definitely not the value of the next node.那绝对不是下一个节点的价值。

Summary概括

You will replace the node at the address of p with the next node in the list.您将用列表中的下一个节点替换p地址处的节点。 When you start you have:当你开始时,你有:

        +----+   +----+
        |    |   |    |
     -->|  p |-->|next|-->
        |    |   |    |
        +----+   +----+
        0x1234
        

After you replace the node at the current address you will have:替换当前地址的节点后,您将拥有:

        +----+
        |    |
     -->|next|-->
        |    |
        +----+
        0x1234
        

Nothing prior to the address of p in the list changes.列表中p的地址之前没有任何变化。 When you are done, you simply:完成后,您只需:

        delete p;

Detail细节

Your question setup tells you you have a pointer p to a node that is not the last node in your list and you need to remove the value at that node.您的问题设置告诉您有一个指针p指向不是列表中最后一个节点的节点,您需要删除该节点处的值。 Great, most of the work is already done, no need to iterate to find the node to remove.太好了,大部分工作已经完成,无需迭代查找要移除的节点。 So how to make use of the current pointer to remove the value (actually replace the node at that address with the next node)?那么如何利用当前指针来移除值(实际上是将该地址处的节点替换为下一个节点)?

Here you can make use of the address of the pointer p ( &p ) and the pointer to the next node ( p->next ) to copy the content of the next node to the present address, overwriting the current node contents with it.在这里,您可以利用指针p ( &p ) 的地址和指向下一个节点的指针 ( p->next ) 将next节点的内容复制到当前地址,并用它覆盖当前节点的内容。 Since you haven't changed where the original pointer p points, it still points to the memory for the original node that was linked at the current address allowing you to free the memory using pointer p to complete the operation.由于您没有更改原始指针p指向的位置,因此它仍然指向链接在当前地址的原始节点的 memory,从而允许您使用指针p释放 memory 以完成操作。

So what you want to do is first, replace the node at the current address in the list, eg所以你要做的是首先,替换列表中当前地址处的节点,例如

node **ppn = &p;               /* a pointer to pointer holding current address */

*ppn = p->next;                /* replace node at address of p with p->next */

(where p points to the current node, &p is the address for the pointer that is linked in your list, and p->next is a separate pointer to the next node in the list) (其中p指向当前节点, &p是列表中链接的指针的地址,而p->next是指向列表中下一个节点的单独指针)

Since the previous next pointer still points to this address, and you have replaced the node at the current address with the next node in the list (whose next pointer still points to the correct following node), this is all that is required to remove the node that was original linked at p in the list.由于前next指针仍然指向这个地址,并且您已经将当前地址的节点替换为列表中的下一个节点(其next指针仍然指向正确的后续节点),这就是删除最初链接到列表中p的节点。

To complete the process and avoid a memory leak, you need to free ( delete ) the memory for the node you removed.要完成该过程并避免 memory 泄漏,您需要释放( delete )您删除的节点的 memory。 Since you have not changed where p itself points, it still points to the node you have removed from your list.由于您没有更改p本身指向的位置,它仍然指向您从列表中删除的节点。 So you can simply:所以你可以简单地:

delete p;

and you are done.你就完成了。

It will take a minute to wrap your head around how you have used the address of p &p as a placeholder in your list, and you have simply replaced the node that was originally at that address in your list , with the next node in your list.您需要花一点时间了解您是如何使用 p &p的地址作为列表中的占位符的,并且您只需将列表中最初位于该地址的节点替换为列表中的下一个节点. This removes the node pointed to by p from the list.这将从列表中删除p指向的节点。 prev->next still points to the address of p and the new node you have assigned to that address still has a valid ->next pointer to the node that follows it in the list, so your list is complete sans one node. prev->next仍然指向p的地址,并且您分配给该地址的新节点仍然有一个有效的->next指针指向列表中跟随它的节点,因此您的列表是完整的,没有一个节点。 The pointer p still points to the memory holding the node that was removed, so you simply delete p;指针p仍然指向 memory 持有被删除的节点,所以你只需delete p; to tidy up.整理。

That is what is explained in Linus on Understanding Pointers , though I never really loved that explanation as I found it a bit thin and awkwardly written.这就是Linus 关于理解指针的解释,尽管我从来没有真正喜欢过这种解释,因为我发现它有点薄而且写得笨拙。

Now you can take this knowledge and look at the del_node() and delnode() functions I pointed you to and begin digesting how it all works.现在您可以利用这些知识并查看我向您指出的del_node()delnode()函数,并开始了解它们是如何工作的。 It may take an hour or a day for the light-bulb to wink on and it all fall into place.灯泡可能需要一个小时或一天的时间才能亮起,然后一切都会到位。 That's fine.没关系。 Thinking about how you can use both the address of a pointer as well as where the pointer points (eg the address it holds as its value) takes a bit of wrestling with to make peace with.考虑如何同时使用指针的地址以及指针指向的位置(例如,它作为其值保存的地址)需要一些努力才能与之和平相处。

Full Example完整示例

A full example using:一个完整的例子:

*ppn = p->next;     /* replace node at address with next */
free (p);           /* delete current */

to remove the 4th node in a list of 1 - 10 using only p and its address:仅使用p及其地址删除1 - 10列表中的第 4 个节点:

#include <stdio.h>
#include <stdlib.h>

typedef struct node_t {
    int data;
    struct node_t *next;
} node_t;

/** add node at end of list, update tail to end */
node_t *add (node_t **head, int v)
{
    node_t **ppn = head,                    /* pointer to pointer to head */
            *pn = *head,                    /* pointer to head */
            *node = malloc (sizeof *node);  /* allocate new node */
 
    if (!node) {                            /* validate allocation */
        perror ("malloc-node");
        return NULL;
    }
    node->data = v;                         /* initialize members values */
    node->next = NULL;
 
    while (pn) {
        ppn = &pn->next;
        pn = pn->next;
    }
 
    return *ppn = node;    /* add & return new node */
}

/** print all nodes in list */
void prn (node_t *l)
{
    if (!l) {
        puts ("list-empty");
        return;
    }
    for (node_t *n = l; n; n = n->next)
        printf (" %d", n->data);
    putchar ('\n');
}

/** delete all nodes in list */
void del_list (node_t *l)
{
    node_t *n = l;
    while (n) {
        node_t *victim = n;
        n = n->next;
        free (victim);
    }
}

int main (void) {
    
    node_t *list = NULL, *p = NULL, **ppn = NULL;
    int node_to_rm = 4;
    
    for (int i = 0; i < 10; i++)
        if (!add (&list, i + 1))
            return 1;
    
    prn (list);
    
    /* iterate to find 4th node (with data = 5) */
    for (ppn=&list, p=list; p && node_to_rm; ppn=&p->next, p=p->next)
        node_to_rm--;
    
    *ppn = p->next;     /* replace node at address with next */
    free (p);           /* delete current */
    
    prn (list);
    del_list (list);
}

Example Use/Output示例使用/输出

$ ./bin/ll_rm_single_node_given_p
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 6 7 8 9 10

Memory Use/Error Check Memory 使用/错误检查

$ valgrind ./bin/ll_rm_single_node_given_p
==22684== Memcheck, a memory error detector
==22684== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22684== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==22684== Command: ./bin/ll_rm_single_node_given_p
==22684==
 1 2 3 4 5 6 7 8 9 10
 1 2 3 4 6 7 8 9 10
==22684==
==22684== HEAP SUMMARY:
==22684==     in use at exit: 0 bytes in 0 blocks
==22684==   total heap usage: 11 allocs, 11 frees, 1,184 bytes allocated
==22684==
==22684== All heap blocks were freed -- no leaks are possible
==22684==
==22684== For counts of detected and suppressed errors, rerun with: -v
==22684== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM