Leetcode 876.求链表的中间节点
一、题目描述
难度: easy 标签:链表
给定一个带有头结点 head
的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
二、自己的解法
这是一道较为基础的链表题,解决的思路有很多种,最容易想到的便是:利用两个指针,一前一后,前面的指针每次走两步,后面的指针每次走一步,前面的指针到达链表末尾时,后面的指针则指向链表中间节点。
实现代码如下:
private static ListNode middleNode(ListNode head){
ListNode forward = head;
ListNode back = head;
while (back != null && back.next != null) {
forward = forward.next;
back = back.next.next;
}
return forward;
}
复杂度分析: 只遍历了一次链表,因此时间复杂度是 O(n),并且只使用了常数量级的空间,因此空间复杂度是 O(1).
运行结果: 空间和时间均击败 100 %,是一个很不错的解法。
三、官方题解
1. 将链表输出至数组中
思路非常的简单,遍历一次链表,将链表放到数组中,然后根据下标求数组的中间位置的节点,代码如下:
public ListNode middleNode(ListNode head) {
ListNode[] A = new ListNode[100];
int t = 0;
while (head.next != null) {
A[t++] = head;
head = head.next;
}
return A[t / 2];
}
另外我还想到了一种方式,那就是将链表节点存储到 Map 中,key 是下标, value 是节点,然后根据 k 的值去查对应的节点,和上面的思路是一样的,只是存放节点的容器不一样,代码如下:
private static ListNode middleNode(ListNode head){
Map<Integer, ListNode> map = new HashMap<>();
int k = 1;
while (head != null){
map.put(k++, head);
head = head.next;
}
return map.get((k + 1) / 2);
}
复杂度分析: 还是只遍历了一次链表,因此时间复杂度是 O(n)。利用了额外的数组或者 Map,容量是链表节点的长度,所以空间复杂度也是 O(n)。
运行结果: 时间和空间均击败 100%。
2. 快慢指针
这就是我上面提到的自己的解法,思路稍微有一些巧妙,总体来说是最优解。