997 字
5 分钟
寻找单链表的倒数第 K 个节点:双指针间隔法
题目描述
给定一个单链表的头节点 head 和一个正整数 k,返回链表中倒数第 k 个节点。
输入:head = 1→2→3→4→5, k = 2
输出:4→5
解释:倒数第 2 个节点是 4解题思路
暴力法的问题
最直接的想法是先遍历一遍求出链表长度 n,然后再从头走 n - k 步。但这需要两次遍历。
双指针间隔法(一次遍历)
想象两个人在一条笔直的马路上赛跑:A 先跑出去 K 米,然后 B 才开始跑。当 A 到达终点时,B 距离终点恰好是 K 米——也就是”倒数第 K 米”的位置。
对应到链表:
fast指针先走k步- 然后
slow和fast同步前进(每次都走一步) - 当
fast到达null(末尾)时,slow恰好指向倒数第k个节点
算法步骤
- fast 指针先走 k 步
- 如果 fast 在走 k 步的过程中变为 null → k 超出链表长度,返回 null
- slow 和 fast 同步前进,直到 fast 指向 null
- 返回 slow(此时 slow 指向倒数第 k 个节点)
关键细节:fast 应该走到哪里?
假设链表长度为 n,倒数第 k 个节点是正数第 n - k 个(从 1 开始编号)。
- fast 先走 k 步,到达第 k+1 个节点
- slow 从第 1 个开始
- 当 fast 走到第 n+1 个(null)时,slow 同步走到
(n+1) - k = n - k + 1个 - 这恰好是倒数第 k 个 ✓
代码实现
#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode {
int val;
struct ListNode *next;
} ListNode;
ListNode* findKthFromEnd(ListNode* head, int k) {
ListNode *fast = head;
ListNode *slow = head;
// fast 先走 k 步
for (int i = 0; i < k; i++) {
// k 超出链表长度
if (!fast) return NULL;
fast = fast->next;
}
// slow 和 fast 同步前进
while (fast) {
slow = slow->next;
fast = fast->next;
}
// fast 到达 null 时,slow 指向倒数第 k 个节点
return slow;
}struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* findKthFromEnd(ListNode* head, int k) {
ListNode *fast = head;
ListNode *slow = head;
// fast 先走 k 步
for (int i = 0; i < k; i++) {
// k 超出链表长度
if (!fast) return nullptr;
fast = fast->next;
}
// slow 和 fast 同步前进
while (fast) {
slow = slow->next;
fast = fast->next;
}
// fast 到达 null 时,slow 指向倒数第 k 个节点
return slow;
}
};class ListNode {
constructor(val, next) {
this.val = (val === undefined ? 0 : val);
this.next = (next === undefined ? null : next);
}
}
function findKthFromEnd(head, k) {
let fast = head;
let slow = head;
// fast 先走 k 步
for (let i = 0; i < k; i++) {
// k 超出链表长度
if (!fast) return null;
fast = fast.next;
}
// slow 和 fast 同步前进
while (fast) {
slow = slow.next;
fast = fast.next;
}
// fast 到达 null 时,slow 指向倒数第 k 个节点
return slow;
}图解过程
输入:head = 1→2→3→4→5, k = 2
初始:fast 和 slow 都在头节点
fast
slow
↓
1 → 2 → 3 → 4 → 5
Step 1: fast 先走 k=2 步
fast
↓
1 → 2 → 3 → 4 → 5
↑
slow
Step 2: slow 和 fast 同步走
fast
↓
1 → 2 → 3 → 4 → 5 → null
↑
slow
当 fast == null 时,slow 指向节点 4(倒数第 2 个节点)复杂度分析
| 维度 | 分析 |
|---|---|
| 时间复杂度 | O(n) — 只遍历一次链表 |
| 空间复杂度 | O(1) — 只用了两个指针 |
关键要点
- 间隔的思想:fast 和 slow 始终保持 k 步的间隔,这是双指针技巧的核心
- 边界处理:fast 先走 k 步的过程中,如果中途变为 null,说明 k > n,需返回 null
- 一次遍历:相比”先求长度再走 n-k 步”的两次遍历,双指针只需一次
扩展思考
- 如果要删除倒数第 k 个节点(LeetCode 19),如何利用本题思路找到倒数第 k+1 个节点?
- 如果链表是双向链表,能否有更优解法?
相关文章:
- 寻找链表的中点 — 同样是双指针,但这里是”速度差”而非”位置差”
寻找单链表的倒数第 K 个节点:双指针间隔法
https://www.hehonglei.cn/posts/linked-list-kth-from-end/