1.移除链表元素
思路一:遍历整个链表
①给定val等于当前节点的val,就释放节点,把该节点前一个节点和该节点后一个节点链接
②给定val不等于当前节点的val,就继续往后走
由于释放节点后我们需要链接前一个节点和后一个节点,因此需要定义一个指针变量prev,记录当前节点的上一个节点
情况一:给定的val不等于当前节点val
情况二:给定的val等于当前节点val
①让prev指向该节点的下一个节点 prev->next = cur->next
②释放当前节点 free(cur)
③cur往后走 cur = prev->next
最后返回头节点
但是提交时会发现一个问题就是第一个节点的val就是要删除的数据时(head == cur或者prev == NULL),prev->next会产生空指针解引用行为而导致错误,因此我们需要特殊处理一下
这时只需要释放头节点让cur向后走,并且更新头节点
正确代码
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* cur = head;
struct ListNode* prev = NULL;
while (cur)
{
if (cur->val == val)
{
if (head == cur)
{
cur = cur->next;
free(head);
head = cur;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}
思路二:尾插
建立一个新的链表,释放val值等于给定val的节点,不等于给定val值的节点依次尾插到新的链表,返回新链表的头
初始情况: 新链表的头和尾指针都赋值成空指针
①第一个节点val值不等于6,则尾插,由于newhead和cur开始为空,因此这次尾插时 newhead = tail = cur,然后cur = cur->next, cur指向了第二个节点
②第二个节点val不等于6,尾插,这时只需要tail->next = cur,便实现了尾插,同时让tail = cur,使得tail时刻指向新链表的尾节点
③第三个节点val等于6,释放该节点,并让cur指向下一个节点,当然要先设置一个变量del 保存下一个节点地址,然后释放该节点, 再让cur =del
后面的操作思路也是一样的,就不一一介绍了
但是如果这样直接提交,会发现不能通过,会报内存错误,而内存错误一般都是野指针这种错误n那么为什么会出现野指针的问题呢?
我们来看当最后一个节点是要删除的节点会发生什么?
5不是要删除的节点,尾插到新链表中,但是原本5这个节点的next保存了下一个节点的地址,也就是最后一个节点的地址,因此将6节点释放后,tail就会成为野指针,因为我们最后要将tail->next置空
但是又出现了新的问题,根据题中给的示例以及提示的错误信息,我们发现是当传入链表为空时,while循环没进去,直接来到tail->next = NULL这条语句,tail就是空指针,因此产生了空指针解引用问题
所以可以在开始加上判断头为空直接返回空的条件判断,我们也可以在最后给tail判空的时候进行条件判断
正确代码
struct ListNode {
int val;
struct ListNode *next;
};
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)
{
struct ListNode* del = cur->next;
free(cur);
cur = del;
}
else
{
if(newhead == NULL)
{
newhead = tail = cur;
cur = cur->next;
}
else
{
tail->next = cur;
tail = cur;
cur = cur->next;
}
}
}
if(tail)
tail->next = NULL;
return newhead;
}
思路三:带哨兵位头节点
带哨兵位头节点的链表这种结构往往能给我们带来很多的优势,我们看到了在思路一中,我们必须考虑到当要删除元素的节点是头节点时的处理办法,确实有些麻烦,我们可以强行在题中所给链表加一个带哨兵位头节点(malloc创建并链接链表),这时要注意prev的初值要改为guard
最后记得释放创建的guard并置空(leetcode不会检查,但这是一种良好的习惯哦)
正确代码
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
guard->next = head;
struct ListNode* cur = head;
struct ListNode* prev = guard;
while (cur)
{
if (cur->val == val)
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
else
{
prev = cur;
cur = cur->next;
}
}
head = guard->next;
free(guard);
guard = NULL;
return head;
}