标题:链表的中间结点
出处:876. 链表的中间结点
3 级
给定一个头结点为 head \texttt{head} head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入: head = [1,2,3,4,5] \texttt{head = [1,2,3,4,5]} head = [1,2,3,4,5]
输出: [3,4,5] \texttt{[3,4,5]} [3,4,5]
示例 2:
输入: head = [1,2,3,4,5,6] \texttt{head = [1,2,3,4,5,6]} head = [1,2,3,4,5,6]
输出: [4,5,6] \texttt{[4,5,6]} [4,5,6]
解释:由于该链表有两个中间结点,值分别为 3 \texttt{3} 3 和 4 \texttt{4} 4,我们返回第二个结点。
由于链表的中间结点的编号和链表的结点数量相关,因此可以首先遍历链表得到链表的结点数量,然后计算链表的中间结点的编号,再定位到链表的中间结点。
假设链表的结点数量是 n n n,头结点到尾结点的编号依次是 1 1 1 到 n n n。当 n n n 分别是奇数和偶数时,链表的中间结点的编号如下:
当 n n n 是奇数时,链表有一个中间结点,其编号是 n + 1 2 = n − 1 2 + 1 \dfrac{n+1}{2}=\dfrac{n-1}{2}+1 2n+1=2n−1+1;
当 n n n 是偶数时,链表有两个中间结点,第二个中间结点的编号是 n 2 + 1 \dfrac{n}{2}+1 2n+1。
当 n n n 是偶数时, n n n 个结点的链表和 n + 1 n+1 n+1 个结点的链表的中间结点的编号相同。对任意正整数 n n n,中间结点的编号可以表示成 ⌊ n 2 ⌋ + 1 \Big\lfloor\dfrac{n}{2}\Big\rfloor+1 ⌊2n⌋+1。
因此在得到链表的结点数量 n n n 之后,从头结点开始,向后移动 ⌊ n 2 ⌋ \Big\lfloor\dfrac{n}{2}\Big\rfloor ⌊2n⌋ 次,即可定位到链表的中间结点。
class Solution {
public ListNode middleNode(ListNode head) {
int n = 0;
ListNode temp = head;
while (temp != null) {
n++;
temp = temp.next;
}
int middleCount = n / 2 + 1;
ListNode middle = head;
for (int i = 1; i < middleCount; i++) {
middle = middle.next;
}
return middle;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表一次得到链表的结点数量,然后遍历链表定位到链表的中间结点。
空间复杂度: O ( 1 ) O(1) O(1)。
得到链表的中间结点的另一种解法是快慢指针,使用快慢指针解法不需要事先知道链表的结点数量。
快慢指针的做法是:创建快指针和慢指针,初始时都指向头结点,然后每次将快指针往后移动 2 2 2 步,将慢指针往后移动 1 1 1 步,直到快指针到达链表的尾结点或者空结点(尾结点的后一个结点为空结点),此时慢指针指向的结点就是链表的中间结点。
对于快慢指针解法的正确性证明,分别考虑链表的结点数量 n n n 是奇数和偶数的情况。
当 n n n 是奇数时,快指针会移动到链表的尾结点,一共移动 n − 1 n-1 n−1 步,慢指针移动 n − 1 2 \dfrac{n-1}{2} 2n−1 步,因此当移动结束时,慢指针位于第 n + 1 2 \dfrac{n+1}{2} 2n+1 个结点,是链表的中间结点。
当 n n n 是偶数时,快指针会移动到链表的空结点,一共移动 n n n 步,慢指针移动 n 2 \dfrac{n}{2} 2n 步,因此当移动结束时,慢指针位于第 n 2 + 1 \dfrac{n}{2}+1 2n+1 个结点,是链表的中间结点。
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。快指针遍历链表一次,慢指针遍历链表的前半段一次。
空间复杂度: O ( 1 ) O(1) O(1)。