给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点
思路 : 通过遍历链表,找到不是val的结点,然后尾插到新链表.
那么思考一个问题,尾插到新链表的时候,需不需要保存下一个结点? 答案是不需要.因为尾插到新链表只是将新的头结点指向了不是val的结点,原链表继续指向该结点.
而尾插的实现最好不要使用学习单链表时候的尾插.因为学习到的是创建一个新结点尾插,而在这结点已经存在,就不需要创建.所以这里的尾插仅仅只是指向结点.
当尾插的时候,每次需要遍历找到尾结点,但这样就会导致时间复杂度提高变为O(n²).为了避免,可以在新链表中再创建一个指针tail,来跟随新链表的尾结点,每插入一个结点就更新一次.
代码如下:
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* newhead = NULL, *tail = NULL;
struct ListNode* cur = head;
while(cur)
{
//不是val的结点取下来尾插
if(cur->val != val)
{
//尾插
if(tail == NULL)
{
newhead = tail = cur;
}
else
{
tail->next = cur;
tail = tail->next;
}
cur=cur->next;
}
else
{
struct ListNode* tmp = cur;
cur = cur->next;
free(tmp);
}
}
return newhead;
}
写到这里还有一个小问题,出现了一个野指针,按照上面的图片尾插后,5结点的next依旧指向下一个6结点.全程并没有改变tail的next指向.并且当链表为空的时候,while循环就不会执行.那么tail也就为空,所以需要加上判断.
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* newhead = NULL, *tail = NULL;
struct ListNode* cur = head;
while(cur)
{
//不是val的结点取下来尾插
if(cur->val != val)
{
//尾插
if(tail == NULL)
{
newhead = tail = cur;
}
else
{
tail->next = cur;
tail = tail->next;
}
cur=cur->next;
}
else
{
struct ListNode* tmp = cur;
cur = cur->next;
free(tmp);
}
}
if(tail) //判断不为空
{
tail->next = NULL;
}
return newhead;
}
这道题就基本上结束了,但还可以进行拓展.单链表分为带头和不带头,这里的头代表的是哨兵位的头结点.
这个结点不存储有效数据,那么链表为空时,不带哨兵位的plist就指向空,带哨兵位的任何情况下都不会指向空
那么多这一个哨兵位和没有,有什么区别呢?这样就不需要使用二级指针了,因为头插需要改变plist的指向, 所以必须使用二级指针.当使用了哨兵位后,就不用改变plist的指向,只有改变哨兵位的指向,也就是结构体指针的指向.
但在单链表的习题中普遍不带有哨兵位,避免先入为主.
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* newhead = NULL, *tail = NULL;
struct ListNode* cur = head;
newhead = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
while(cur)
{
//不是val的结点取下来尾插
if(cur->val != val)
{
tail->next = cur;
tail = tail->next;
cur = cur->next;
}
else
{
struct ListNode* tmp = cur;
cur = cur->next;
free(tmp);
}
}
tail->next = NULL; //有了哨兵位tail就不再为空
struct ListNode* tmp = newhead->next; //临时保存
newhead = newhead->next;
free(newhead); //不能直接释放,否则无法返回
return newhead->next;
}