目录
1、题目
我们通过阅读题干,可以得到的信息有:传入的数据有,一个链表的头结点,一个用了来判断的val值。需要返回的是完成删除之后的新链表头结点。
2、思路
我们继续秉持先一般后特殊的思考方向。我们平时在删除链表元素的过程当中,一般是先让前一个元素的next指针,指向当前元素的next。即:跳过当前元素,使得前一元素和后一元素直接连接。
在链接完成之后,释放中间结点(free操作),即可完成链表内一个中间元素的删除。
但是在考虑过一般情况过后,我们已经知道中间元素是val值的情况怎么处理,但是如果链表首结点是val呢?直接删除首结点会造成一些意想不到的情况,因为首结点没有前一结点!那么我们需要针对头结点进行特殊处理,譬如:1、特殊判断头指针的情况。2、在首结点之前虚拟一个头结点,从而把首结点也当作一个中间元素来处理。
好了,特殊情况和一般情况我们都考虑到过后,我们就可以上手进行实践,来检验我们的算法思想到底正不正确。
3、实现
本篇文章里采用的是,针对首结点进行特殊判断的方法。即:如果首结点的val符合题意,则将首结点的next结点,设置为新的head结点,从而可以删除值为val的首结点。
struct ListNode* prev = NULL;
struct ListNode* cur = head;
创建两个链表结构体指针,prev在cur之前。cur就是题目传给我们的head结点,prev暂时置为NULL。
while(cur)
{
if(cur->val == val)
{
//删除代码
}
else
{
prev = cur;
cur = cur->next;
}
}
创建一个循环,当cur的val和val相等,执行删除结点的操作。如果不相等,则需要判断下一个结点的情况,prev前进到cur指针的位置,cur指针指向下一个位置。
//删除代码
if(cur == head)//如果首结点是val,要特殊处理
{
head = cur->next;
free(cur);
cur = head;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
在删除代码在书写的时候,我们要分一般情况和特殊情况来讨论。即:特殊:首结点值为val。一般:首结点以外的值为val。
1、如果首结点的值为val。head指针指向cur的next元素,即调整首结点到下一个结点。释放之前cur指针指向的head空间,再将cur指针指向新的head。
2、首结点以外的值为val。将prev指针的next,指向cur指针的next,相当于直接跳过cur指针指向的元素,释放cur指针指向的空间,再将cur指针指向prev指针的next(cur永远都在prev的后面)。
4、结果与总结
leetcode通过。
我们再来回顾一下该题的一些算法思想和要点。即建立双指针,永远保持一前一后的状态(cur在前,prev在后)。cur指针用来判断当前元素的值是否符合val,prev指针用来在符合题意的情况下,删除cur指针指向的结点(链表的删除一定要知道前一结点的位置!)。
在该题当中,特殊情况就在于判断首结点的值,除题解之外我们也可以采用一种通法来解决,即:创建一个虚拟头指针,将链表内结点全部变为普通结点,从而实现统一操作。
-1、完整代码
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = NULL;
struct ListNode* cur = head;
while(cur)
{
if(cur->val == val)
{
//判断首结点的情况
if(cur == head)
{
head = cur->next;
free(cur);
cur = head;
}
//删除
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}