题目描述
给你单链表的头结点 head
,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:head = [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点,值为 3 。
示例 2:
输入:head = [1,2,3,4,5,6] 输出:[4,5,6] 解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。
提示:
- 链表的结点数范围是
[1, 100]
1 <= Node.val <= 100
这题也是使用双指针方法(快慢指针)
我们使用两个指针来遍历链表:
- 快指针(fast pointer):每次移动两个节点。
- 慢指针(slow pointer):每次移动一个节点。
当快指针到达链表末尾时,慢指针正好指向链表的中间节点。
NOTE:题目要求:「两个中间结点的时候,返回第二个中间结点」。快指针可以前进的条件是:当前快指针和当前快指针的下一个结点都非空。如果题目要求「在两个中间结点的时候,返回第一个中间结点」,此时快指针可以前进的条件是:当前快指针的下一个结点和当前快指针的下下一个结点都非空 。
为什么快指针和慢指针能找到中间节点?
- 当快指针一次移动两个节点,慢指针一次移动一个节点时,快指针的移动速度是慢指针的两倍。
- 因此,当快指针到达链表末尾时,慢指针刚好走到链表的中间位置。
细节解释
1. 两个中间节点时,返回第二个中间节点
当链表长度为偶数时,例如 [1, 2, 3, 4, 5, 6]
:
- 第一次:
slow
在1,fast
在1 - 第二次:
slow
在2,fast
在3 - 第三次:
slow
在3,fast
在5 - 第四次:
slow
在4,fast
超出链表末尾
此时,快指针和快指针的下一个节点都非空,所以可以继续前进。在返回时,slow
指向的是第4个节点,即第二个中间节点。
2. 两个中间节点时,返回第一个中间节点
如果要求返回第一个中间节点,可以改变快指针的前进条件:
- 快指针前进的条件是当前快指针的下一个节点和下下一个节点都非空。
完整代码:
// 定义链表节点结构
struct ListNode {
int val; // 节点值
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
class Solution {
public:
ListNode* middleNode(ListNode* head) {
// 定义慢指针和快指针,初始化都指向链表头节点
ListNode *slow = head;
ListNode *fast = head;
// 当快指针和快指针的下一个节点都不为空时,循环继续
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next; // 快指针每次移动两个节点
slow = slow->next; // 慢指针每次移动一个节点
}
// 当快指针到达链表末尾时,慢指针指向的就是链表的中间节点
return slow;
}
};