如何从链表中弹出?
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
的同一个对象。
【讨论】:
你“想”?所以,你不确定你在回答什么。以上是关于如何从链表中弹出?的主要内容,如果未能解决你的问题,请参考以下文章