
该方法利用递归调用的“回溯”特性:先层层深入至链表尾部(递归基触发),再在逐层返回过程中累加索引,从而自然实现从后往前定位第 k 个节点。
该方法利用递归调用的“回溯”特性:先层层深入至链表尾部(递归基触发),再在逐层返回过程中累加索引,从而自然实现从后往前定位第 k 个节点。
在链表中查找倒数第 k 个节点时,直观思路常是“先遍历一遍得长度,再走 length−k+1 步”。但递归解法巧妙绕过显式长度计算——它不靠正向推进,而是依赖函数调用栈的压入与弹出顺序完成逆序计数。
关键在于理解这行代码的执行时序:
<code class="java">int index = printKthToLast(head.next, k) + 1;</code>
它并非“边递归边打印”,而是必须等待 printKthToLast(head.next, k) 完全返回后,才执行 + 1 并继续后续逻辑。因此整个过程分为两个阶段:
递归下沉(Downward Phase):
从头节点出发,不断调用printKthToLast(head.next, k),直至head == null—— 此时抵达链表末端,触达递归终止条件,返回0。-
回溯计数(Upward/Return Phase):
每次函数返回时,上层调用将子调用结果+ 1,得到当前节点距离链表尾部的位置(即“倒数索引”)。例如:- 尾节点(40)返回
0 + 1 = 1→ 倒数第 1 位; - 前一节点(30)收到
1后返回1 + 1 = 2→ 倒数第 2 位; - 此时
index == k成立,立即打印head.data(即 30)。
- 尾节点(40)返回
以链表 10 → 20 → 30 → 40、k = 2 为例,调用栈与 index 演变如下:
| 调用层级 | head.data |
printKthToLast(head.next, k) 返回值 |
index = ... + 1 |
是否触发打印? |
|---|---|---|---|---|
head=40 |
40 |
printKthToLast(null, 2) → 0
|
0 + 1 = 1 |
否(1 ≠ 2) |
head=30 |
30 | 上层返回 1
|
1 + 1 = 2 |
✅ 是(2 == 2) |
head=20 |
20 | 上层返回 2
|
2 + 1 = 3 |
否(3 ≠ 2) |
head=10 |
10 | 上层返回 3
|
3 + 1 = 4 |
否(4 ≠ 2) |
⚠️ 注意事项:
- 此方法时间复杂度为 O(n),空间复杂度为 O(n)(递归栈深度);
- 若需常数空间,应改用双指针迭代法;
- 代码未处理
k > 链表长度的边界情况,实际应用中建议增加校验(如返回-1或抛异常);index表示的是“当前节点是倒数第几个”,而非正向位置,这是理解回溯逻辑的核心。
本质上,你并未显式“告诉程序从结尾开始”,而是通过递归终止于空节点 + 在返回路径上累加,让系统自动构建出一条由底向上的计数通路——这正是递归“延迟计算”魅力的典型体现。










