目录
1.前言
本题题目来源:删除链表的中间节点

输入:head = [1,3,4,7,1,2,6]
输出:[1,3,4,1,2,6]
解释:
上图表示给出的链表。节点的下标分别标注在每个节点的下方。
由于 n = 7 ,值为 7 的节点 3 是中间节点,用红色标注。
返回结果为移除节点后的新链表。

输入:head = [1,2,3,4]
输出:[1,2,4]
解释:
上图表示给出的链表。
对于 n = 4 ,值为 3 的节点 2 是中间节点,用红色标注。
关于链表中间节点的寻找,小编的上期博客中有超详细干货讲解,如果友友们有疑惑的,可以去看看喔,看完小编相信友友们对于这类问题肯定有更清晰的见解!!!
[力扣题目]寻找链表的中间节点,超详细干货讲解,快慢法手把手教会!!-CSDN博客
2.实现方法
在删除节点中,我们常用的方法就是修改指向:让被删除节点的前驱节点直接指向被删除节点的后继节点,这样我们就完成了节点的删除
2.1一般方法
核心思想:找到链表的中间节点,然后根据这个中间节点找到中间节点的前驱节点,最后修改指向即可完成中间节点的删除.这个思路是我们面对这个问题一般的解决思路.
- 寻找链表的中间节点
关于中间节点的寻找,小编在上期博客中向友友们介绍了两种方法:
1.遍历法
2.快慢引用法
小编在这里用效率更高的快慢引用法来寻找中间节点作为演示.


小编把寻找中间节点的代码附上:
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
此时的slow就是中间节点.
在这里面:
fast = fast.next.next;
- 作用:fast每次走两步
slow = slow.next;
- 作用:slow每次走一步
循环的条件:fast != null && fast.next != null;
- 这是fast能安全走两步的前提条件,两个条件之间不可调换顺序,都是为了防范空指针异常
2.寻找中间节点的前驱节点
ListNode curNode = head;
//寻找前驱节点
while(curNode.next != slow){
curNode = curNode.next;
}
在这段代码中:
ListNode curNode = head;
- 作用:保存头结点,并且curNode指向头结点,方便后面寻找中间节点的前驱节点.
while(curNode.next != slow){
curNode = curNode.next;//curNode不断向后移动,直到找到前驱节点为止
}
- 作用:寻找中间节点的前驱节点,该循环的终止条件是:curNode.next == slow,循环结束后的curNode就是中间节点的前驱节点.
3.修改指向
curNode.next = curNode.next.next;
curNode是中间节点的前驱节点,可以这样修改指向,也可以是下面的这种方式.
curNode.next = slow.next;
slow是中间节点,可以通过slow来访问到slow的后继节点.
上面是一般情况的讨论,下面我们再来讨论一下边界情况
if(head == null || head.next == null){
return null;
}
- 当链表为空的时候,此时直接返回null即可
- 当链表只有一个节点的时候,中间节点就是它本身,把它删除之后,也就是null了,也是直接返回null即可.另外当只有一个节点的时候,也没有前驱节点这个说法.
- 这两种情况可以利用逻辑运算符|| ,放在一起讨论.
以上就是这个方法的深度剖析了小编把这个方法完整的代码附上.
class Solution {
public ListNode deleteMiddle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode curNode = head;
ListNode curN = null;
//找前驱节点
while(curNode.next != slow){
curNode = curNode.next;
}
curNode.next = curNode.next.next;
return head;
}
}
2.2快慢引用一次遍历法
在上面的方法中,我们先要遍历一次链表拿到链表的中间节点,然后再遍历一次链表拿到中间节点的前驱节点.虽然时间复杂度上还是O(n).
那有没有只需要遍历一次链表的方法呢? 有的兄弟,有的!!!!
在寻找中间节点的过程中:
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
在这里我们思考:能不能再有一个节点,在slow移动之前保留好slow?当在循环结束之后,这个节点就是slow的前驱节点,也就是中间节点的前驱节点了.
ListNode fast = head;
ListNode slow = head;
ListNode prev = null;
while(fast != null && fast.next != null){
prev = slow;
fast = fast.next.next;
slow = slow.next;
}
prev.next = slow.next;
在这段代码中:
ListNode prev = null;
- 作用:定义一个prev节点,用来保存每次slow移动之前的节点.
循环的条件:fast != null && fast.next != null;
- 作用:是fast能安全走两步的前提条件,两个条件的顺序不能调换,都是为了防范空指针异常
prev = slow;
- 作用:保留好每次slow移动之前的位置,当在循环结束之后,prev也就是中间节点slow的前驱节点了.
当拿到中间节点的前驱节点之后,我们就可以修改指向了
prev.next = slow.next;
- 作用:修改前驱节点的指向
也可以是下面这种方式修改指向
prev.next = prev.next.next;
通过上面的层层剖析,我们已经搞定这个方法了,各位友友们是不是觉得很简单!!!! 相信友友们已经拿捏这个方法了!!!
这个方法的边界情况讨论还是和上面的方法一样
if(head == null || head.next == null){
return null;
}
- 当链表为空的时候,直接返回null即可
- 点链表只有一个节点的时候,这个节点也就是唯一的中间节点,删除之后链表是null了,也是直接返回null即可.
最后小编把这个方法完整的代码附上:
class Solution {
public ListNode deleteMiddle(ListNode head) {
if(head == null || head.next == null){
return null;
}
//其他情况
ListNode fast = head;
ListNode slow = head;
ListNode prev = null;
while(fast != null && fast.next != null){
prev = slow;
fast = fast.next.next;
slow = slow.next;
}
prev.next = slow.next;
return head;
}
}
以上就是这两种方法的分享了,希望可以帮助到处在困惑期的友友们!!!
3.总结
今天分享的是删除链表中间节点的两种方法,方法一是先利用快慢法来找到链表的中间节点,然后根据这个中间节点来找到前驱节点,最后修改前驱节点的指向从而来完成中间节点的删除;方法二是在快慢法寻找中间节点的过程中,利用一个prev节点来记录好每次slow移动之前的位置,达到一定条件之后这个prev也就是前驱节点了最后也是修改前驱节点的指向从而来完成中间节点的删除.
希望这两种方法可以给友友们带来帮助,如果觉得小编写的还行,麻烦动动小手给小编一键三连喔!!!!!您的点赞 评论 收藏都是小编创作源源不断的动力!!!!
希望可以与各位友友们共同努力,共同进步,在代码的海洋中不断前行探索!!!!

1391

被折叠的 条评论
为什么被折叠?



