LeetCode 面试题 02.02. 返回倒数第 k 个节点
题目描述

题意分析
在单链表中找到倒数第
k个节点,返回它保存的值。倒数从一开始计数,倒数第一个是尾节点,倒数第链表长度个是头节点。题目保证
k有效,因此目标一定存在。需要区分这个接口与返回节点对象的题目:定位过程相同,但这里最终返回的是节点值。
解法:快慢指针固定间距
核心思路
[!blue]
单链表只能向后走,无法从尾部反向数节点。可以用两个指针形成固定间距,让前面的指针负责判断什么时候到达链尾,后面的指针自然停在目标位置。
两个指针先都指向头节点,让
fast单独走恰好k次后继。此时从slow出发走k次就能到达fast;之后两个指针每轮同时前进一步,这个距离始终不变。循环以
fast到达尾节点之后的空位置为终点。此时从slow到空位置恰好还需走k次后继,说明从slow开始直到尾节点共有k个节点,所以slow正是倒数第k个。预走
k步和终点选择空节点必须配套。若k等于链表长度,预走后fast已为空,slow不再移动,正好返回头节点;若k == 1,两个指针相差一步,快指针越过尾部时慢指针就停在尾节点。
解题步骤
- 令
fast、slow都指向头节点。- 将
fast向后移动恰好k步,建立固定间距。- 只要
fast非空,就让两个指针各向后移动一步。- 循环结束后返回
slow.val。
代码实现
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
// 快指针先走 k 步,以空节点作为最终参照
for (int i = 0; i < k; i++) {
fast = fast.next;
}
// 同步推进保持间距,快指针到空时慢指针即为目标
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow.val;
}
}
func kthToLast(head *ListNode, k int) int {
fast, slow := head, head
// 快指针先走 k 步,以空节点作为最终参照
for i := 0; i < k; i++ {
fast = fast.Next
}
// 同步推进保持间距,快指针到空时慢指针即为目标
for fast != nil {
fast = fast.Next
slow = slow.Next
}
return slow.Val
}
复杂度分析
设链表长度为 $n$。
- 时间复杂度:$O(n)$,快指针总共走到链尾后的空位置,慢指针至多移动 $n-k$ 次。
- 空间复杂度:$O(1)$,只维护两个指针,不修改链表。
关键点总结
[!green]
- 用固定间距把“从尾部计数”转成“前指针到终点时后指针的位置”。
- 先走
k步对应以空节点为终点,二者必须成套使用。k合法时目标自然存在,返回它的值即可。
易错点总结
[!yellow]
- 先走
k - 1步却仍等快指针到空位置,会让慢指针多走一步。- 先走
k步后又在快指针到尾节点时停止,会提前结束,得到目标前一个节点。- 同步阶段只移动一个指针,会破坏固定间距。
- 循环条件应先判断快指针是否为空,不能在空指针上访问后继。
- 不要返回节点对象或头节点值,接口要求的是定位后慢指针的节点值。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 剑指 Offer 22. 链表中倒数第k个节点 | 简单 | 定位倒数第k项的方法相同,原题返回节点本身,本题返回节点值,接口不能直接混用。 |
| 19. 删除链表的倒数第 N 个结点 | 中等 | 原题还要删除倒数第n项,需定位前驱,本题只读取目标节点。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!