力扣链接:203. 移除链表元素 - 力扣(LeetCode)
这道题目涉及到删除节点,回忆下链表的结构,每个链表的节点除了存储数据外,还存储了下一个节点的地址,也就是说我们删除掉一个节点的时候,我们首先要找到这个节点的上一个节点,这里我们叫它为prev,接着还需要它的下一个节点,我们叫它为next。因为链表是通过一个一个的next指针链接的,如果这里直接把要删除的节点释放掉,就访问不到它后面的节点了,因为这里涉及到野指针问题。所以在删除节点前,要让这个节点的prev的next指向这个节点的next。
这道题目可以用三种方法,我们首先讲解第一种尾插的方法。
方法一:取不需要删除的节点依次尾插
这里的cur在2那里
这里的cur在3那里
这种方法需要在最后额外让tail的next置为NULL(前提是tail不为空),防止出现野指针的情况。
还有就是尾插第一个的时候其实是让newhead和tail都等于当前的cur。
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* newhead = NULL;
struct ListNode* tail = NULL;
struct ListNode* cur = head;
while (cur)
{
if (cur->val != val)
{
if (newhead)
{
tail->next = cur;
tail = cur;
}
else
newhead = tail = cur;
cur = cur->next;
}
else
{
// 释放节点前保存下一个节点
struct ListNode* tmp = cur->next;
free(cur);
cur = tmp;
}
}
if (newhead)
tail->next = NULL;
return newhead;
}
方法二:双指针,需要删除时创建第三个
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = NULL;
struct ListNode* cur = head;
struct ListNode* next;
while (cur)
{
if (cur ->val != val)
{
prev = cur; // prev始终保持在cur的前一个节点
cur = cur->next;
}
else
{
next = cur->next;
free(cur);
cur = next;
if (prev == NULL) // 当第一个节点就要删除时更新头节点
head = next;
else
prev->next = next;
}
}
return head;
}
方法三:增加哨兵位
这种方法与方法2类似,只是增加了哨兵位,可以少判断一次prev为不为空
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* phead = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur = head;
struct ListNode* prev = phead;
struct ListNode* next = NULL;
while (cur)
{
if (cur ->val != val)
{
prev->next = cur;
prev = cur;
cur = cur->next;
}
else
{
next = cur->next;
free(cur);
cur = next;
prev->next = next;
}
}
// 防止头节点为空
prev->next = NULL;
// 不要忘记释放malloc出来的哨兵位
head = phead->next;
free(phead);
return head;
}