题目描述
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。(题目来源)
思路一:
要移除链表中值为val的节点,我们肯定是要将链表遍历一遍的,关键是我们在遍历中如何操作是一个问题。所有,我们考虑问题的时候,可以先考虑比较常见的情况,再考虑特殊情况。
1.考虑常见情况:
要移除某一节点,也就是让该节点的前一个节点指向待移除节点的后一个节点,然后将待移除节点释放即可。这里呢,我们可以定义三个指针变量:prev,cur,next.
prev(previous):记录待排查节点的前一个节点的位置。
cur(current):记录当前正在排查的节点位置。
next(next):记录待排查节点的后一个节点。
当cur指针指向的节点并非待移除的节点时,3个节点依次向后移动。
当cur指针指向待移除的节点时,我们首先让Prev指针指向的节点指向next,然后把cur指针指向的节点释放掉,并将next指针赋值给cur指针,next指针向后移动。
如此进行下去,直到链表遍历完毕,值val的节点也就删除完了。
2.考虑特殊情况:
常见的请款的分析往往只能解决问题的一般情况,并不能解决问题的极端情况。要真正的解决问题,我们需要考虑到的问题的极端情况。如,当待移除的节点时第一个节点或者是最后一个节点的情况,当链表为空的情况。
2.1当链表的第一个节点为待移除的节点时:
这时我们需要先将头指针指向Next,然后释放掉cur所指向的节点,并将next指针赋值给cue指针,next再后移。
2.2当链表的最后一个节点为待移除的节点时:
当排查到最后一个节点时,cur指向最后一个节点,next指针指向该节点的位置,即NULL.
我们上面常规情况的方法对其分析,发现常规情况的思路适用于这种特殊情况,并且发现遍历的终止条件,就是当cur为NULL时遍历停止。
2.3当传入的链表为空时:
我们会发现,如果传来的是空链表,cur指针的值一开始就为空,而我们遍历的终止条件就是当cur为NULL时停止遍历,所以,如果传来的是空链表,直接执行到函数末尾,即返回头指针。(NULL)
代码实现
/** * Definition for singly-linked list. * struct ListNode * { * int val; * struct ListNode *next; * }; */ struct ListNode* removeElements(struct ListNode* head, int val) { struct ListNode*prev=NULL;//记录待排查节点的前一个节点位置 struct ListNode*cur=head;//记录当前正在排查的节点位置 while(cur!=NULL)//当cur为空时,循环停止 { if(cur->val==val)//当前排查的节点时待移除的节点 { struct ListNode*next=cur->next;//记录待排查节点的后一个节点位置 if(cur==head)//带移除节点是链表的第一个节点 { head=next;//头指针指向next free(cur);//释放掉第一个节点 cur=next;//将Next指针赋值给cur指针 } else //带移除的节点不是链表的第一个节点 { prev->next=next;//prev指针指向的节点指向next free(cur);//将cur指针指向的节点释放掉 cur=next;//将next指针赋值给cur指针 } } else//当前排查的节点不是待移除的节点 { prev=cur;//指针后移 cur=cur->next;//指针后移 } } return head;//返回新的头指针 }
思路二:
思路一可能过于麻烦,当我们要移除某一个节点时,还需要判断该节点是否为第一个节点,那么有没有什么办法可以不进行这个操作呢?
回答是肯定的。新的解决办法就是在传入的链表前面强行加上一个头节点,并让链表原来的头指针指向该头节点,这样我们就不用判断待移除的节点是否为第一个节点了(因为现在第一个节点就是头节点)。
在加了头节点后,我们就只需要根据常见情况的逻辑进行代码编写。但是有一点需要注意的是,遍历完链表后要将头节点指向的位置(即第一个节点的位置)赋值给头指针,并将头节点释放掉,最后才能返回头指针。
代码实现
/** * Definition for singly-linked list. * struct ListNode * { * int val; * struct ListNode *next; * }; */ struct ListNode* removeElements(struct ListNode* head, int val) { struct ListNode*guard=(struct ListNode*)malloc(sizeof(struct ListNode));//申请一个头节点,返回其地址 guard->next=head;//让头节点指向链表的第一个节点 struct ListNode*prev=guard;//prev指针指向头节点 struct ListNode*cur=guard->next;//cur指针指向原链表的第一个节点 while(cur!=NULL)//当cur为空时,循环停止 { if(cur->val==val)//当前排查的节点时待移除的节点 { struct ListNode*next=cur->next;//记录待排查节点的后一个节点位置 prev->next=next;//prev指针指向的节点指向next free(cur);//将cur指针指向的节点释放掉 cur=next;//将next指针赋值给cur指针 } else//当前排查的节点不是待移除的节点 { prev=cur;//指针后移 cur=cur->next;//指针后移 } } head=guard->next;//将头节点指向的位置赋值给头指针,使头指针指向链表第一个节点 free(guard);//释放头节点 guard=NULL;//及时置空 return head;//返回新的头指针 }
解题结果如下:
文章到这里就结束了,有更好的思路或问题的,欢迎留言评论区。
下期预告:拿捏链表(二)-------- 反转链表