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

如图所示,图中红色的节点便是中间节点
可以看到在奇数和偶数节点数量的两种不同情况下,对中间节点的定义是有所不一样的
当是奇数个节点的时候,直接取中间那个节点;当是偶数个节点的时候,取中间节点的第二个
老规矩,小编在给各位友友们手搓两个链表,希望通过这种图解的方式,给友友们带来清晰易懂的讲解.


每个节点上方的数字是该节点在栈上的引用(小编随便起的),指向堆内存中的真实节点对象.
2.实现方法
2.1遍历法
观察上面两个链表,我们不难发现链表的长度和中间节点的下标存在一定关系
在第一个链表中,链表的长度为5,链表中间节点的下标为2 --->=length / 2 = 5 / 2;(除法会取整)
在第二个链表中,链表的长度为4,链表中间节点的下标为2---->= length / 2 = 4 / 2;
基于前面两种情况,我们可以总结出:当链表长度不管是奇数还是偶数时,中间节点的下标都是链表长度的一半,即length / 2;
所以第一种遍历法的核心思路就是:先遍历一次链表得到链表长度length,再遍历(length / 2)步得到中间节点.(在这里,以步为单位更能体现这个寻找过程,例如在第一个链表中,链表中间节点的下标是2,也就是从头结点开始需要走两步才能到中间节点)
求链表长度,只要当前节点不是null,我们就让计数器+1,小编把这部分代码附上
int length = 0;//定义一开始的长度
ListNode curNode = head;//保存头结点
while(curNode != null){
length ++;//只要当前节点不是null,我们就让length++
curNode = curNode.next;//节点继续向后移动
}
接下来就是再遍历一次链表,走length / 2 步得到中间节点,小编也把代码附上
curNode = head;
int count = length / 2;
while(count != 0){
curNode = curNode.next;
count --;
}
return curNode;
经过走length / 2 步之后,此时curNode顺利的来到了中间节点的位置,直接返回curNode即可.
这时,可能有的友友们会有疑问:curNode = head;为什么会有这行代码呢?这行代码有什么用呢?
小编在这里给友友们娓娓道来
在前面求链表长度中,我们使用到了curNode这个节点,当求完链表的长度之后,curNode最终会变成null,而在求中间节点的过程中,我们还是需要从头开始遍历的,此时直接用curNode是不行的,因为此时curNode == null,我们只需要再拿到一开始的头结点 即curNode = head,把头结点head重新赋值给curNode即可
那有友友们会有疑惑:为什么不直接使用head节点呢?
在这里,如果直接使用head,head这个引用会发生变化,而非一开始指向头结点了,当我们后续要再想访问到头结点时会造成一定的困难.
这里,你也可以让计数器count++,当count等于length / 2时,退出循环,本质上都是走length / 2步
处理完一般情况后,我们再来处理一下边界情况的
当链表为空时,此时是否还有中间节点?答案是没有的
通过上面这些,这个方法已经被我们搞定了,小编把这个方法完整的代码的附上
class Solution {
public ListNode middleNode(ListNode head) {
//当链表为空的时候
if(head == null ){
return head;
}
//其他情况
int length = 0;
ListNode curNode = head;
//求链表的长度
while(curNode != null){
length ++;
curNode = curNode.next;
}
curNode = head;
int count = length / 2;
//遍历寻找中间节点
while(count != 0){
curNode = curNode.next;
count --;
}
return curNode;
}
}
2.2快慢引用法
在上面的遍历法中,我们需要遍历两次链表才能得到中间节点,这个方法很暴力,小编不太推荐这个方法,那有没有只需要遍历一次链表就能得到中间节点的呢? 有的兄弟,有的!!!!!
所谓快慢引用,肯定是有一个走的快(fast),另一个走的慢(slow)
在c/c++中,叫快慢指针法,指针是用来存放地址的;在Java中,叫快慢引用,引用是用来存放地址的,两者在方法上没有什么本质区别.
首先我们先得把fast和slow定义出来先,让fast和slow都指向一开始的head
ListNode fast = head;
ListNode slow = head;
核心思想:fast每次走两步,slow每次走一步,当fast达到条件时,此时的slow即是中间节点
那有友友们问:为什么是这样子做的呢?这个方法背后的原理是什么呢?
这个方法的数学原理如下:当fast的速度为2,slow的速度为1时,在相同时间内,当fast走到终点时,slow走的路程刚好是fast所走路程的一半,slow此时在整段路程的中间位置,即链表的中间节点处
小编接下来会给各位友友们讲的明明白白,让大家彻底吃透这个方法!!
小编再次手搓链表,并把每一步的过程展示出来,希望通过这种清晰的方式,帮助各位友友们理解.
链表长度为奇数情况下:

第一次移动:fast走两步,slow走一步

第二次移动:fast走两步,slow走一步

此时slow已经来到了中间节点的位置,fast节点来到了尾节点,fast节点的特点是fast.next == null;
链表长度为偶数情况下:

第一次移动:fast走两步,slow走一步

第二次移动:fast走两步,slow走一步

此时slow顺利的来到了中间节点的位置,fast节点此时的特点是:fast == null;
通过上面两种奇数长度下和偶数长度下的讨论,我们可以总结出slow节点来到中间节点的条件是:
fast.next == null 或者fast == null,也就是说当fast != null 并且 fast.next != null的时候,我们都要不停的重复fast走两步,slow走一步,即要通过循环来解决这个问题.
那有友友们会疑惑不是分两种情况的吗,不应该是fast != null 或者 fast.next != null 吗?
在这里,如果fast 已经等于空了即fast == null ,那再访问fast.next 会报出空指针异常,这个问题后面也会探讨到
那到底是怎么实现fast一次走两步,slow一次走一步的呢?小编把代码附上
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
在这里,小编觉得循环的条件很巧妙,这个条件更多是fast能走两步的前提条件,但也和前面两种情况中fast的结束条件有关,不知道各位友友们有没有感受到这一点.
要想访问到fast.next ,fast不能为空,即fast != null;
要想访问到fast.next.next,fast.next不能为空,即fast.next != null;
这两个很好地防范了空指针异常的问题,所以要两个条件同时成立,即fast != null && fast.next != null;
那有友友们问:那我两个条件调换一下顺序有问题吗?即fast.next != null && fast != null,可以吗?
在链表这一类问题中,尤其要特别注意防范空指针异常的问题,这个问题可以说是重中之重!!!
当两个条件调换之后,循环的条件是(fast.next!= null&& fast != null), 如果fast == null 的时候,会首先判断fast.next 是否为空,此时就会报出空指针异常.
在解决完一般情况之后,我们再来处理一下边界情况:当链表为空的时候,直接返回即可
这个方法也已经被我们搞定了,小编把完整的代码附上
class Solution {
public ListNode middleNode(ListNode head) {
//判断链表是否为空
if(head == null ){
return head;
}
//定义两个快慢引用
ListNode fast = head;
ListNode slow = head;
//利用循环
while(fast != null && fast.next != null){
fast = fast.next.next;//fast每次走两步
slow = slow.next;//slow每次走一步
}
return slow;//当fast达到循环的结束条件之后,此时的slow就是中间节点
}
}
相信各位友友们肯定对这种方法有了深深的感受,这种利用快慢的思想在链表之类的题目,很多都可以实践到,友友们可以学到满满的干货喔!!!!
3.总结
关于今天的分享主要介绍了寻找链表中间节点的两种方法,第一种是遍历思路,先求出链表的长度
然后利用length / 2 来得到链表的中间节点;第二种方法是利用快慢引用法,两个引用一快一慢,当fast达到一定条件时,此时的slow就是所要求的中间节点.
最后希望可以与各位友友们共同努力,共同进步,有什么问题,欢迎各位友友们在评论区批评指正喔!!
小编期待你的精彩发言.
638

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



