876. 链表的中间结点
简 单 \color{#00AF9B}{简单} 简单
给定一个头结点为
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,我们返回第二个结点。
提示:
- 给定链表的结点数介于
1
和100
之间。
方法1:快慢双指针
这个方法的思路比较简单,我们定义快慢指针从头结点head
开始。快指针fast
每次循环走2步,慢指针slow
每次循环走1步。这样一来,当fast
遍历到链表结尾时(fast
或者fast->next
为空,因为要走2步,得确保fast->next
不为空),slow
指向的就是链表的中间项了。
每次循环内fast
和slow
的变化情况:
fast = fast->next->next
slow = slow->next
题目要求当链表长度为偶数时,返回中间2个结点的后一个结点。我们来验证一下这个思路是否能满足题目要求。
- 假设链表长度为奇数,例如
[1,2,3]
,fast
在前进2步后指向了3
,此时slow
前进1步指向了2
,结果正确; - 假设链表长度为偶数,例如
[1,2,3,4]
,fast
前进2步后指向了3
,由于此时fast
(3)和fast->next
(4)都不为空,因此还能前进1轮,指向了4
的下一个结点,也就是空。slow
也跟着前进了2轮,也就是2步,指向了3
。满足指向中间结点2
和3
的后面一个,结果正确。
由此可知,我们的思路可以满足题目要求。
class Solution
{
public:
ListNode *middleNode(ListNode *head)
{
ListNode *fast = head, *slow = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
};
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)。我们只需要常量空间存放若干变量。
参考结果
Accepted
36/36 cases passed (0 ms)
Your runtime beats 100 % of cpp submissions
Your memory usage beats 69.69 % of cpp submissions (6.9 MB)
19. 删除链表的倒数第 N 个结点
中 等 \color{#FFB800}{中等} 中等
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
方法1:快慢双指针
我们可以使用快慢指针的思路来解决这道题。
我们先思考一下如何找到要删除的结点。设置两个指针fast
和slow
,fast
先前进n
步。这样fast
就比slow
先行了n
步,随后fast
和slow
同步前进,直至fast
遍历到数组末尾,即fast
为空指针,此时slow
指向的结点就是我们要删除的结点了。
由于题目给出的链表为单向链表,因此不能访问前一个结点,我们其实要找被删除结点的前一个结点。为了使程序照常进行,我们只需要定义一个新的傀儡结点dummy
,它指向的结点为头结点head
。这样一来,fast
指向head
,slow
指向dummy
,再按上述过程进行遍历,最后slow->next
即为要删除的结点。
注:以下代码中释放了目标结点和dummy
结点的空间。至于是否释放是根据题目要求或者面试官要求来确定的,请大家一定要注意这一点。本题中没有规定,大家是否释放看自己的心情吧(
class Solution
{
public:
ListNode *removeNthFromEnd(ListNode *head, int n)
{
ListNode *dummy = new ListNode{0, head};
ListNode *fast = head, *slow = dummy;
for (int i = 0; i < n; i++)
{
fast = fast->next;
}
while (fast)
{
fast = fast->next;
slow = slow->next;
}
ListNode *target = slow->next;
slow->next = slow->next->next;
ListNode *ans = dummy->next;
delete target, dummy;
return ans;
}
};
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)。我们只需要常量空间存放若干变量。
参考结果
Accepted
208/208 cases passed (4 ms)
Your runtime beats 75.85 % of cpp submissions
Your memory usage beats 17.93 % of cpp submissions (10.5 MB)