如何从链表中弹出?

Posted

技术标签:

【中文标题】如何从链表中弹出?【英文标题】:How to pop from linked list? 【发布时间】:2013-08-29 15:36:50 【问题描述】:

我在 C 中实现了一个带有 Pop 函数的 Linked-List:

Node * pop (Node * head) 
    Node * temp = head;

    printf("Temp is: %s\n", temp->val);

    if (head->next != NULL) 
        *head = *head->next;
    

    printf("Temp is: %s\n", temp->val);
    return temp;

当我弹出时的输出会是这样的:

Temp is: node1 value
Temp is: node2 value

也就是说,当我分配*head = *head->next时,temp变成了temp->next。

那么如何获取当前head 的值并将其返回,同时还将链接列表的头部移动到head->next

执行head = head->next 不会删除对第一个节点的引用。 (即当我打印列表时,第一个节点仍然存在)。

【问题讨论】:

【参考方案1】:
void pop(struct node** tol) 
  struct node* t = *tol;
  while (t->link->link != NULL)
    t = t->link;    
  
  t->link = NULL;

【讨论】:

【参考方案2】:

指针按值传递。也就是说,当您将指针传递给堆栈时,被调用函数对指针指向的内容的更改不会反映在调用函数中。

为了在调用函数中改变节点指针的值,需要将栈作为指向指针的指针传递:

Node* pop (Node** head) 

    Node* temp = *head;    

    if (temp) 
       *head = temp->next;    // to update stack in calling function
       temp->next = NULL;     // to detach temp from the rest of the list
    

    return temp;

在更新*head 的值之前,您不需要检查if ((*head)->next) 或在本例中为if (temp->next),因为如果您位于堆栈的最后一个节点并且下一个节点是NULL,您想要无论如何,列表是NULL

Karthik T 的答案正确解释了为什么 temp 的值在您的原始代码中发生了变化。

【讨论】:

【参考方案3】:

首先,请注意您的代码(以及之前的一些解决方案)永远不会弹出列表中的最后一个元素。你想要的

if (*head != NULL) ...

接下来,将指针传递给指针将起作用。但实际上最好是这样制作一个列表标题:

typedef struct node_s 
  struct node_s *next;
  ... data declaration here
 Node;

typedef struct list_s 
  struct node_s *head;
 List;

void init_list(List *list) 
  list->head = NULL;

现在声明一个这样的列表:

List list[1];

init_list(list);

声明一个包含一个元素的数组会使每个对list 的引用自动成为一个指针,从而消除代码中的大量&。那么实现push和pop就很干净了:

void push(List *list, Node *node) 
  node->next = list->head;
  list->head = node;


Node *pop(List *list) 
  Node *head = list->head;
  if (head) 
    list->head = head->next;
    head->next = NULL;
  
  return head;

为什么这样更好?假设您稍后决定保留列表中的项目计数。使用单独的标头节点,这很容易:

typedef struct list_s 
  struct node_s *head;
  int length;
 List;

void init_list(List *list) 
  list->head = NULL;
  length = 0;


void push(List *list, Node *node) 
  node->next = list->head;
  list->head = node;
  ++list->length;


Node *pop(List *list) 
  Node *head = list->head;
  if (head) 
    list->head = head->next;
    head->next = NULL;
    --list->length;
  
  return head;

请注意,无需更改调用代码。使用指向指针方法的指针,您处于死胡同。还有许多其他用例,其中具有单独的列表标题可以使您的代码更灵活地适应未来的变化。

【讨论】:

【参考方案4】:

您需要将head 的地址传递给您的函数来修改它。然后你的函数需要取消引用这个地址。此外,最后一个 pop() 也需要更改 *AddressOfHead

Node *pop(Node **AddressOfHead) 
    Node *temp = *AddressOfHead;
    if (temp) 
        *AddressOfHead = temp->next;
    
    return temp;

...

// Usage example
Node *TopOfList = pop(&Head);

【讨论】:

@Travv92 仔细测试。如果您从 NULL 头开始并推送节点,这将永远不会弹出最后一个节点。 @Gene 你能解释一下为什么会发生这种情况吗?我这里有一些代码确实可以正确弹出最后一个节点:ideone.com/tVYck0。 (请忽略我没有释放分配的内存的事实) @verbose 有人解决了这个问题!现在好了。我仍然认为使用指针指向指针而不是真正的列表头会使代码变脆。 @Gene 的优缺点。拥有 Head 结构可能很有用。如果需要的不仅仅是非零值,那么具有长度的头部结构可以提供快速的长度评估。对于其他标记信息也很有用。带有长度的头结构是多余的,因为列表以 NULL 结尾并且一致性成为一个问题。带长度的头部占用 2 倍空间。当可能存在大量空列表时,这很重要。顺便说一句:我儿子最近在平原上度过了一天。 @chux 好的,但是四个字节很少是决定性的,而将大量列表操作更改为新的约定通常是。如果你喜欢无头列表,你应该使用循环,外部指针 P 指向尾部,所以 P->next 是头部。然后你可以免费排队。恭喜你的普莱贝。我们可能有他在 IT105。如果没有,那就下学期。【参考方案5】:

别人已经告诉你怎么解决了,让我来回答为什么temp变了..

Node * pop (Node * head) 

您将 head 作为指向 Node 的指针传递。

所以当你这样做时

*head = *head->next;

我认为它被解析为

 *head = *(head->next);

因此将 next 中的对象复制到 head 的对象中,这当然是 temp 的同一个对象。

【讨论】:

你“想”?所以,你不确定你在回答什么。

以上是关于如何从链表中弹出?的主要内容,如果未能解决你的问题,请参考以下文章

从堆栈列表(链表)中弹出一个数字?

如何从链表中反转堆栈?

递归地从链表中删除数据

如何从链表中出列时释放内存?

何时以及如何将HashMap从链表转换为红黑树? [重复]

从链表中删除节点